Skip to content

Commit bb084f9

Browse files
legoaterbroonie
authored andcommitted
spi: aspeed: Adjust direct mapping to device size
The segment registers of the FMC/SPI controllers provide a way to configure the mapping window of the flash device contents on the AHB bus. Adjust this window to the size of the spi-mem mapping. Things get more complex with multiple devices. The driver needs to also adjust the window of the next device to make sure that there is no overlap, even if there is no available device. The proposal below is not perfect but it is covering all the cases we have seen on different boards with one and two devices on the same bus. Reviewed-by: Joel Stanley <[email protected]> Tested-by: Joel Stanley <[email protected]> Tested-by: Tao Ren <[email protected]> Tested-by: Jae Hyun Yoo <[email protected]> Signed-off-by: Cédric Le Goater <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 9da06d7 commit bb084f9

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

drivers/spi/spi-aspeed-smc.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,92 @@ static int aspeed_spi_chip_set_default_window(struct aspeed_spi_chip *chip)
411411
return chip->ahb_window_size ? 0 : -1;
412412
}
413413

414+
static int aspeed_spi_set_window(struct aspeed_spi *aspi,
415+
const struct aspeed_spi_window *win)
416+
{
417+
u32 start = aspi->ahb_base_phy + win->offset;
418+
u32 end = start + win->size;
419+
void __iomem *seg_reg = aspi->regs + CE0_SEGMENT_ADDR_REG + win->cs * 4;
420+
u32 seg_val_backup = readl(seg_reg);
421+
u32 seg_val = aspi->data->segment_reg(aspi, start, end);
422+
423+
if (seg_val == seg_val_backup)
424+
return 0;
425+
426+
writel(seg_val, seg_reg);
427+
428+
/*
429+
* Restore initial value if something goes wrong else we could
430+
* loose access to the chip.
431+
*/
432+
if (seg_val != readl(seg_reg)) {
433+
dev_err(aspi->dev, "CE%d invalid window [ 0x%.8x - 0x%.8x ] %dMB",
434+
win->cs, start, end - 1, win->size >> 20);
435+
writel(seg_val_backup, seg_reg);
436+
return -EIO;
437+
}
438+
439+
if (win->size)
440+
dev_dbg(aspi->dev, "CE%d new window [ 0x%.8x - 0x%.8x ] %dMB",
441+
win->cs, start, end - 1, win->size >> 20);
442+
else
443+
dev_dbg(aspi->dev, "CE%d window closed", win->cs);
444+
445+
return 0;
446+
}
447+
448+
/*
449+
* Yet to be done when possible :
450+
* - Align mappings on flash size (we don't have the info)
451+
* - ioremap each window, not strictly necessary since the overall window
452+
* is correct.
453+
*/
454+
static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip,
455+
u32 local_offset, u32 size)
456+
{
457+
struct aspeed_spi *aspi = chip->aspi;
458+
struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 };
459+
struct aspeed_spi_window *win = &windows[chip->cs];
460+
int ret;
461+
462+
aspeed_spi_get_windows(aspi, windows);
463+
464+
/* Adjust this chip window */
465+
win->offset += local_offset;
466+
win->size = size;
467+
468+
if (win->offset + win->size > aspi->ahb_window_size) {
469+
win->size = aspi->ahb_window_size - win->offset;
470+
dev_warn(aspi->dev, "CE%d window resized to %dMB", chip->cs, win->size >> 20);
471+
}
472+
473+
ret = aspeed_spi_set_window(aspi, win);
474+
if (ret)
475+
return ret;
476+
477+
/* Update chip mapping info */
478+
chip->ahb_base = aspi->ahb_base + win->offset;
479+
chip->ahb_window_size = win->size;
480+
481+
/*
482+
* Also adjust next chip window to make sure that it does not
483+
* overlap with the current window.
484+
*/
485+
if (chip->cs < aspi->data->max_cs - 1) {
486+
struct aspeed_spi_window *next = &windows[chip->cs + 1];
487+
488+
/* Change offset and size to keep the same end address */
489+
if ((next->offset + next->size) > (win->offset + win->size))
490+
next->size = (next->offset + next->size) - (win->offset + win->size);
491+
else
492+
next->size = 0;
493+
next->offset = win->offset + win->size;
494+
495+
aspeed_spi_set_window(aspi, next);
496+
}
497+
return 0;
498+
}
499+
414500
static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
415501
{
416502
struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->master);
@@ -425,6 +511,8 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
425511
if (op->data.dir != SPI_MEM_DATA_IN)
426512
return -EOPNOTSUPP;
427513

514+
aspeed_spi_chip_adjust_window(chip, desc->info.offset, desc->info.length);
515+
428516
if (desc->info.length > chip->ahb_window_size)
429517
dev_warn(aspi->dev, "CE%d window (%dMB) too small for mapping",
430518
chip->cs, chip->ahb_window_size >> 20);

0 commit comments

Comments
 (0)