summaryrefslogtreecommitdiffstats
path: root/src/drivers/spi/cbfs_spi.c
blob: 5b16d18150b15dbf998f35a93756a177b5cd1747 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* SPDX-License-Identifier: GPL-2.0-only */
/* This file is part of the coreboot project. */

/*
 * This file provides a common CBFS wrapper for SPI storage. SPI driver
 * context is expanded with the buffer descriptor used to store data read from
 * SPI.
 */

#include <boot_device.h>
#include <console/console.h>
#include <spi_flash.h>
#include <symbols.h>
#include <cbmem.h>
#include <stdint.h>
#include <timer.h>

static struct spi_flash spi_flash_info;
static bool spi_flash_init_done;

/*
 * SPI speed logging for big transfers available with BIOS_DEBUG. The format is:
 *
 * read SPI 0x62854 0x7db7: 10416 us, 3089 KB/s, 24.712 Mbps
 *
 * The important number is the last one. It should roughly match your SPI
 * clock. If it doesn't, your driver might need a little tuning.
 */
static ssize_t spi_readat(const struct region_device *rd, void *b,
				size_t offset, size_t size)
{
	struct stopwatch sw;
	bool show = size >= 4 * KiB && console_log_level(BIOS_DEBUG);

	if (show)
		stopwatch_init(&sw);
	if (spi_flash_read(&spi_flash_info, offset, size, b))
		return -1;
	if (show) {
		long usecs;

		usecs = stopwatch_duration_usecs(&sw);
		u64 speed;	/* KiB/s */
		int bps;	/* Bits per second */

		speed = size * (u64)1000 / usecs;
		bps = speed * 8;

		printk(BIOS_DEBUG, "read SPI %#zx %#zx: %ld us, %lld KB/s, %d.%03d Mbps\n",
		       offset, size, usecs, speed, bps / 1000, bps % 1000);
	}
	return size;
}

static ssize_t spi_writeat(const struct region_device *rd, const void *b,
				size_t offset, size_t size)
{
	if (spi_flash_write(&spi_flash_info, offset, size, b))
		return -1;
	return size;
}

static ssize_t spi_eraseat(const struct region_device *rd,
				size_t offset, size_t size)
{
	if (spi_flash_erase(&spi_flash_info, offset, size))
		return -1;
	return size;
}

/* Provide all operations on the same device. */
static const struct region_device_ops spi_ops = {
	.mmap = mmap_helper_rdev_mmap,
	.munmap = mmap_helper_rdev_munmap,
	.readat = spi_readat,
	.writeat = spi_writeat,
	.eraseat = spi_eraseat,
};

static struct mmap_helper_region_device mdev =
	MMAP_HELPER_REGION_INIT(&spi_ops, 0, CONFIG_ROM_SIZE);

static void switch_to_postram_cache(int unused)
{
	/*
	 * Call boot_device_init() to ensure spi_flash is initialized before
	 * backing mdev with postram cache. This prevents the mdev backing from
	 * being overwritten if spi_flash was not accessed before dram was up.
	 */
	boot_device_init();
	if (_preram_cbfs_cache != _postram_cbfs_cache)
		mmap_helper_device_init(&mdev, _postram_cbfs_cache,
					REGION_SIZE(postram_cbfs_cache));
}
ROMSTAGE_CBMEM_INIT_HOOK(switch_to_postram_cache);

void boot_device_init(void)
{
	int bus = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS;
	int cs = 0;

	if (spi_flash_init_done == true)
		return;

	if (spi_flash_probe(bus, cs, &spi_flash_info))
		return;

	spi_flash_init_done = true;

	mmap_helper_device_init(&mdev, _cbfs_cache, REGION_SIZE(cbfs_cache));
}

/* Return the CBFS boot device. */
const struct region_device *boot_device_ro(void)
{
	if (spi_flash_init_done != true)
		return NULL;

	return &mdev.rdev;
}

/* The read-only and read-write implementations are symmetric. */
const struct region_device *boot_device_rw(void)
{
	return boot_device_ro();
}

const struct spi_flash *boot_device_spi_flash(void)
{
	boot_device_init();

	if (spi_flash_init_done != true)
		return NULL;

	return &spi_flash_info;
}