• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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