1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 /*
4 * This file provides a common CBFS wrapper for SPI storage. SPI driver
5 * context is expanded with the buffer descriptor used to store data read from
6 * SPI.
7 */
8
9 #include <boot_device.h>
10 #include <cbfs.h>
11 #include <console/console.h>
12 #include <spi_flash.h>
13 #include <symbols.h>
14 #include <stdint.h>
15 #include <timer.h>
16
17 static struct spi_flash spi_flash_info;
18 static bool spi_flash_init_done;
19
20 /*
21 * SPI speed logging for big transfers available with BIOS_DEBUG. The format is:
22 *
23 * read SPI 0x62854 0x7db7: 10416 us, 3089 KB/s, 24.712 Mbps
24 *
25 * The important number is the last one. It should roughly match your SPI
26 * clock. If it doesn't, your driver might need a little tuning.
27 */
spi_readat(const struct region_device * rd,void * b,size_t offset,size_t size)28 static ssize_t spi_readat(const struct region_device *rd, void *b,
29 size_t offset, size_t size)
30 {
31 struct stopwatch sw;
32 bool show = size >= 4 * KiB && console_log_level(BIOS_DEBUG);
33
34 if (show)
35 stopwatch_init(&sw);
36 if (spi_flash_read(&spi_flash_info, offset, size, b))
37 return -1;
38 if (show) {
39 long usecs;
40
41 usecs = stopwatch_duration_usecs(&sw);
42 u64 speed; /* KiB/s */
43 int bps; /* Bits per second */
44
45 speed = size * (u64)1000 / usecs;
46 bps = speed * 8;
47
48 printk(BIOS_DEBUG, "read SPI %#zx %#zx: %ld us, %lld KB/s, %d.%03d Mbps\n",
49 offset, size, usecs, speed, bps / 1000, bps % 1000);
50 }
51 return size;
52 }
53
spi_writeat(const struct region_device * rd,const void * b,size_t offset,size_t size)54 static ssize_t spi_writeat(const struct region_device *rd, const void *b,
55 size_t offset, size_t size)
56 {
57 if (spi_flash_write(&spi_flash_info, offset, size, b))
58 return -1;
59 return size;
60 }
61
spi_eraseat(const struct region_device * rd,size_t offset,size_t size)62 static ssize_t spi_eraseat(const struct region_device *rd,
63 size_t offset, size_t size)
64 {
65 if (spi_flash_erase(&spi_flash_info, offset, size))
66 return -1;
67 return size;
68 }
69
70 /* Provide all operations on the same device. */
71 static const struct region_device_ops spi_ops = {
72 .mmap = mmap_helper_rdev_mmap,
73 .munmap = mmap_helper_rdev_munmap,
74 .readat = spi_readat,
75 .writeat = spi_writeat,
76 .eraseat = spi_eraseat,
77 };
78
79 static struct mmap_helper_region_device mdev =
80 MMAP_HELPER_DEV_INIT(&spi_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
81
boot_device_init(void)82 void boot_device_init(void)
83 {
84 int bus = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS;
85 int cs = 0;
86
87 if (spi_flash_init_done == true)
88 return;
89
90 if (spi_flash_probe(bus, cs, &spi_flash_info))
91 return;
92
93 spi_flash_init_done = true;
94 }
95
96 /* Return the CBFS boot device. */
boot_device_ro(void)97 const struct region_device *boot_device_ro(void)
98 {
99 if (spi_flash_init_done != true)
100 return NULL;
101
102 return &mdev.rdev;
103 }
104
105 /* The read-only and read-write implementations are symmetric. */
boot_device_rw(void)106 const struct region_device *boot_device_rw(void)
107 {
108 return boot_device_ro();
109 }
110
boot_device_spi_flash(void)111 const struct spi_flash *boot_device_spi_flash(void)
112 {
113 boot_device_init();
114
115 if (spi_flash_init_done != true)
116 return NULL;
117
118 return &spi_flash_info;
119 }
120