coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
spi_dma.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <amdblocks/lpc.h>
4 #include <amdblocks/spi.h>
5 #include <assert.h>
6 #include <boot_device.h>
8 #include <commonlib/region.h>
9 #include <console/console.h>
10 #include <delay.h>
11 #include <device/pci_ops.h>
12 #include <soc/pci_devs.h>
13 #include <spi_flash.h>
14 #include <string.h>
15 #include <thread.h>
16 #include <types.h>
17 
18 /* The ROM is memory mapped just below 4GiB. Form a pointer for the base. */
19 #define rom_base ((void *)(uintptr_t)(0x100000000ULL - CONFIG_ROM_SIZE))
20 
23  size_t source;
24  size_t size;
25  size_t transfer_size;
26  size_t remaining;
27 };
28 
29 static void *spi_dma_mmap(const struct region_device *rd, size_t offset, size_t size __unused)
30 {
31  const struct mem_region_device *mdev;
32 
33  mdev = container_of(rd, __typeof__(*mdev), rdev);
34 
35  return &mdev->base[offset];
36 }
37 
38 static int spi_dma_munmap(const struct region_device *rd __unused, void *mapping __unused)
39 {
40  return 0;
41 }
42 
43 static ssize_t spi_dma_readat_mmap(const struct region_device *rd, void *b, size_t offset,
44  size_t size)
45 {
46  const struct mem_region_device *mdev;
47 
48  mdev = container_of(rd, __typeof__(*mdev), rdev);
49 
50  memcpy(b, &mdev->base[offset], size);
51 
52  return size;
53 }
54 
55 static bool spi_dma_is_busy(void)
56 {
59 }
60 
61 static bool spi_dma_has_error(void)
62 {
65 }
66 
67 static bool can_use_dma(void *destination, size_t source, size_t size)
68 {
69  /*
70  * Print a notice if reading more than 1024 bytes using mmap. This makes
71  * it easier to debug why the SPI DMA wasn't used.
72  */
73  const size_t warning_size = 1024;
74 
75  if (size < LPC_ROM_DMA_MIN_ALIGNMENT)
76  return false;
77 
78  if (!IS_ALIGNED((uintptr_t)destination, LPC_ROM_DMA_MIN_ALIGNMENT)) {
79  if (size > warning_size)
80  printk(BIOS_DEBUG, "Target %p is unaligned\n", destination);
81  return false;
82  }
83 
84  if (!IS_ALIGNED(source, LPC_ROM_DMA_MIN_ALIGNMENT)) {
85  if (size > warning_size)
86  printk(BIOS_DEBUG, "Source %#zx is unaligned\n", source);
87  return false;
88  }
89 
90  return true;
91 }
92 
93 static void start_spi_dma_transaction(struct spi_dma_transaction *transaction)
94 {
95  uint32_t ctrl;
96 
97  printk(BIOS_SPEW, "%s: dest: %p, source: %#zx, remaining: %zu\n", __func__,
98  transaction->destination, transaction->source, transaction->remaining);
99 
100  /*
101  * We should have complete control over the DMA controller, so there shouldn't
102  * be any outstanding transactions.
103  */
107  assert(transaction->remaining >= LPC_ROM_DMA_MIN_ALIGNMENT);
108 
111  (uintptr_t)transaction->destination);
112 
115 
116  transaction->transfer_size =
119 
120  ctrl |= LPC_ROM_DMA_CTRL_DW_COUNT(transaction->transfer_size);
121  ctrl |= LPC_ROM_DMA_CTRL_ERROR; /* Clear error */
122  ctrl |= LPC_ROM_DMA_CTRL_START;
123 
124  /*
125  * Ensure we have exclusive access to the SPI controller before starting the LPC SPI DMA
126  * transaction.
127  */
129 
131 }
132 
133 /* Returns true if transaction is still in progress. */
134 static bool continue_spi_dma_transaction(const struct region_device *rd,
135  struct spi_dma_transaction *transaction)
136 {
137  /* Verify we are looking at the correct transaction */
139 
140  if (spi_dma_is_busy())
141  return true;
142 
143  /*
144  * Unlock the SPI mutex between DMA transactions to allow other users of the SPI
145  * controller to interleave their transactions.
146  */
148 
149  if (spi_dma_has_error()) {
150  printk(BIOS_ERR, "SPI DMA failure: dest: %p, source: %#zx, size: %zu\n",
151  transaction->destination, transaction->source,
152  transaction->transfer_size);
153  return false;
154  }
155 
156  transaction->destination += transaction->transfer_size;
157  transaction->source += transaction->transfer_size;
158  transaction->remaining -= transaction->transfer_size;
159 
160  if (transaction->remaining >= LPC_ROM_DMA_MIN_ALIGNMENT) {
161  start_spi_dma_transaction(transaction);
162  return true;
163  }
164 
165  if (transaction->remaining > 0) {
166  /* Use mmap to finish off the transfer */
167  spi_dma_readat_mmap(rd, transaction->destination, transaction->source,
168  transaction->remaining);
169 
170  transaction->destination += transaction->remaining;
171  transaction->source += transaction->remaining;
172  transaction->remaining -= transaction->remaining;
173  }
174 
175  return false;
176 }
177 
178 static struct thread_mutex spi_dma_hw_mutex;
179 
180 static ssize_t spi_dma_readat_dma(const struct region_device *rd, void *destination,
181  size_t source, size_t size)
182 {
183  struct spi_dma_transaction transaction = {
185  .source = source,
186  .size = size,
187  .remaining = size,
188  };
189 
190  printk(BIOS_SPEW, "%s: start: dest: %p, source: %#zx, size: %zu\n", __func__,
192 
194 
195  start_spi_dma_transaction(&transaction);
196 
197  do {
198  udelay(2);
199  } while (continue_spi_dma_transaction(rd, &transaction));
200 
202 
203  printk(BIOS_SPEW, "%s: end: dest: %p, source: %#zx, remaining: %zu\n",
204  __func__, destination, source, transaction.remaining);
205 
206  /* Allow queued up transaction to continue */
207  thread_yield();
208 
209  if (transaction.remaining)
210  return -1;
211 
212  return transaction.size;
213 }
214 
215 static ssize_t spi_dma_readat(const struct region_device *rd, void *b, size_t offset,
216  size_t size)
217 {
218  if (can_use_dma(b, offset, size))
219  return spi_dma_readat_dma(rd, b, offset, size);
220  else
221  return spi_dma_readat_mmap(rd, b, offset, size);
222 }
223 
224 const struct region_device_ops spi_dma_rdev_ro_ops = {
225  .mmap = spi_dma_mmap,
226  .munmap = spi_dma_munmap,
227  .readat = spi_dma_readat,
228 };
229 
230 static const struct mem_region_device boot_dev = {
231  .base = rom_base,
232  .rdev = REGION_DEV_INIT(&spi_dma_rdev_ro_ops, 0, CONFIG_ROM_SIZE),
233 };
234 
235 const struct region_device *boot_device_ro(void)
236 {
237  return &boot_dev.rdev;
238 }
239 
241 {
242  table->flash_base = 0;
244  table->size = CONFIG_ROM_SIZE;
245 
246  return 1;
247 }
248 
249 /*
250  * Without this magic bit, the SPI DMA controller will write 0s into the destination if an MMAP
251  * read happens while a DMA transaction is in progress. i.e., PSP is reading from SPI. The bit
252  * that fixes this was added to Cezanne, Renoir and later SoCs. So the SPI DMA controller is not
253  * reliable on any prior generations.
254  */
255 static void spi_dma_fix(void)
256 {
257  /* Internal only registers */
258  uint8_t val = spi_read8(0xfc);
259  val |= BIT(6);
260  spi_write8(0xfc, val);
261 }
262 
264 {
265  spi_dma_fix();
266 }
void * memcpy(void *dest, const void *src, size_t n)
Definition: memcpy.c:7
#define assert(statement)
Definition: assert.h:74
#define IS_ALIGNED(x, a)
Definition: helpers.h:19
#define MIN(a, b)
Definition: helpers.h:37
#define ALIGN_DOWN(x, a)
Definition: helpers.h:18
static struct mmap_helper_region_device mdev
Definition: cbfs_spi.c:79
#define printk(level,...)
Definition: stdlib.h:16
#define BIT(nr)
Definition: ec_commands.h:45
static struct region_device rdev
Definition: flashconsole.c:14
static size_t offset
Definition: flashconsole.c:16
#define __unused
Definition: helpers.h:38
#define container_of(ptr, type, member)
container_of - cast a member of a structure out to the containing structure
Definition: helpers.h:33
static __always_inline void pci_write_config32(const struct device *dev, u16 reg, u32 val)
Definition: pci_ops.h:76
static __always_inline u32 pci_read_config32(const struct device *dev, u16 reg)
Definition: pci_ops.h:58
void thread_mutex_unlock(struct thread_mutex *mutex)
Definition: thread.c:421
void thread_mutex_lock(struct thread_mutex *mutex)
Definition: thread.c:408
int thread_yield(void)
Definition: thread.c:337
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
#define REGION_DEV_INIT(ops_, offset_, size_)
Definition: region.h:87
#define SOC_LPC_DEV
Definition: pci_devs.h:124
#define LPC_ROM_DMA_MIN_ALIGNMENT
Definition: lpc.h:120
#define LPC_ROM_DMA_CTRL_START
Definition: lpc.h:119
#define LPC_ROM_DMA_CTRL_DW_COUNT(bytes)
Definition: lpc.h:121
#define LPC_ROM_DMA_EC_HOST_CONTROL
Definition: lpc.h:113
#define LPC_ROM_DMA_DST_ADDR
Definition: lpc.h:109
#define LPC_ROM_DMA_SRC_ADDR
Definition: lpc.h:108
#define LPC_ROM_DMA_CTRL_MAX_BYTES
Definition: lpc.h:123
#define LPC_ROM_DMA_CTRL_DW_COUNT_MASK
Definition: lpc.h:117
#define LPC_ROM_DMA_CTRL_ERROR
Definition: lpc.h:118
struct thread_mutex spi_hw_mutex
Definition: fch_spi_util.c:10
void spi_write8(uint8_t reg, uint8_t val)
Definition: fch_spi_util.c:43
uint8_t spi_read8(uint8_t reg)
Definition: fch_spi_util.c:28
static bool spi_dma_is_busy(void)
Definition: spi_dma.c:55
static bool continue_spi_dma_transaction(const struct region_device *rd, struct spi_dma_transaction *transaction)
Definition: spi_dma.c:134
static ssize_t spi_dma_readat(const struct region_device *rd, void *b, size_t offset, size_t size)
Definition: spi_dma.c:215
const struct region_device_ops spi_dma_rdev_ro_ops
Definition: spi_dma.c:224
uint32_t spi_flash_get_mmap_windows(struct flash_mmap_window *table)
Definition: spi_dma.c:240
static ssize_t spi_dma_readat_dma(const struct region_device *rd, void *destination, size_t source, size_t size)
Definition: spi_dma.c:180
#define rom_base
Definition: spi_dma.c:19
const struct region_device * boot_device_ro(void)
Definition: spi_dma.c:235
static void * spi_dma_mmap(const struct region_device *rd, size_t offset, size_t size __unused)
Definition: spi_dma.c:29
static ssize_t spi_dma_readat_mmap(const struct region_device *rd, void *b, size_t offset, size_t size)
Definition: spi_dma.c:43
static int spi_dma_munmap(const struct region_device *rd __unused, void *mapping __unused)
Definition: spi_dma.c:38
static bool spi_dma_has_error(void)
Definition: spi_dma.c:61
static bool can_use_dma(void *destination, size_t source, size_t size)
Definition: spi_dma.c:67
static void spi_dma_fix(void)
Definition: spi_dma.c:255
static void start_spi_dma_transaction(struct spi_dma_transaction *transaction)
Definition: spi_dma.c:93
static struct thread_mutex spi_dma_hw_mutex
Definition: spi_dma.c:178
void boot_device_init(void)
Definition: spi_dma.c:263
static const struct mem_region_device boot_dev
Definition: spi_dma.c:230
__SIZE_TYPE__ ssize_t
Definition: stddef.h:13
unsigned int uint32_t
Definition: stdint.h:14
unsigned long uintptr_t
Definition: stdint.h:21
unsigned char uint8_t
Definition: stdint.h:8
struct region_device rdev
Definition: region.h:184
void *(* mmap)(const struct region_device *, size_t, size_t)
Definition: region.h:68
size_t remaining
Definition: spi_dma.c:26
uint8_t * destination
Definition: spi_dma.c:22
size_t transfer_size
Definition: spi_dma.c:25
u8 val
Definition: sys.c:300
void udelay(uint32_t us)
Definition: udelay.c:15