On Wed, 2 Mar 2022 at 17:31, Cédric Le Goater <clg@xxxxxxxx> wrote: > > 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. > > Signed-off-by: Cédric Le Goater <clg@xxxxxxxx> Reviewed-by: Joel Stanley <joel@xxxxxxxxx> > --- > drivers/spi/spi-aspeed-smc.c | 88 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 88 insertions(+) > > diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c > index b4854b521477..974ab215ec34 100644 > --- a/drivers/spi/spi-aspeed-smc.c > +++ b/drivers/spi/spi-aspeed-smc.c > @@ -405,6 +405,92 @@ static int aspeed_spi_chip_set_default_window(struct aspeed_spi_chip *chip) > return chip->ahb_window_size ? 0 : -1; > } > > +static int aspeed_spi_set_window(struct aspeed_spi *aspi, > + const struct aspeed_spi_window *win) > +{ > + u32 start = aspi->ahb_base_phy + win->offset; > + u32 end = start + win->size; > + void __iomem *seg_reg = aspi->regs + CE0_SEGMENT_ADDR_REG + win->cs * 4; > + u32 seg_val_backup = readl(seg_reg); > + u32 seg_val = aspi->data->segment_reg(aspi, start, end); > + > + if (seg_val == seg_val_backup) > + return 0; > + > + writel(seg_val, seg_reg); > + > + /* > + * Restore initial value if something goes wrong else we could > + * loose access to the chip. > + */ > + if (seg_val != readl(seg_reg)) { > + dev_err(aspi->dev, "CE%d invalid window [ 0x%.8x - 0x%.8x ] %dMB", > + win->cs, start, end - 1, win->size >> 20); > + writel(seg_val_backup, seg_reg); > + return -EIO; > + } > + > + if (win->size) > + dev_dbg(aspi->dev, "CE%d new window [ 0x%.8x - 0x%.8x ] %dMB", > + win->cs, start, end - 1, win->size >> 20); > + else > + dev_dbg(aspi->dev, "CE%d window closed", win->cs); > + > + return 0; > +} > + > +/* > + * Yet to be done when possible : > + * - Align mappings on flash size (we don't have the info) > + * - ioremap each window, not strictly necessary since the overall window > + * is correct. > + */ > +static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip, > + u32 local_offset, u32 size) > +{ > + struct aspeed_spi *aspi = chip->aspi; > + struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 }; > + struct aspeed_spi_window *win = &windows[chip->cs]; > + int ret; > + > + aspeed_spi_get_windows(aspi, windows); > + > + /* Adjust this chip window */ > + win->offset += local_offset; > + win->size = size; > + > + if (win->offset + win->size > aspi->ahb_window_size) { > + win->size = aspi->ahb_window_size - win->offset; > + dev_warn(aspi->dev, "CE%d window resized to %dMB", chip->cs, win->size >> 20); > + } > + > + ret = aspeed_spi_set_window(aspi, win); > + if (ret) > + return ret; > + > + /* Update chip mapping info */ > + chip->ahb_base = aspi->ahb_base + win->offset; > + chip->ahb_window_size = win->size; > + > + /* > + * Also adjust next chip window to make sure that it does not > + * overlap with the current window. > + */ > + if (chip->cs < aspi->data->max_cs - 1) { > + struct aspeed_spi_window *next = &windows[chip->cs + 1]; > + > + /* Change offset and size to keep the same end address */ > + if ((next->offset + next->size) > (win->offset + win->size)) > + next->size = (next->offset + next->size) - (win->offset + win->size); > + else > + next->size = 0; > + next->offset = win->offset + win->size; > + > + aspeed_spi_set_window(aspi, next); > + } > + return 0; > +} > + > static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) > { > struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->master); > @@ -419,6 +505,8 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) > if (op->data.dir != SPI_MEM_DATA_IN) > return -EOPNOTSUPP; > > + aspeed_spi_chip_adjust_window(chip, desc->info.offset, desc->info.length); > + > if (desc->info.length > chip->ahb_window_size) > dev_warn(aspi->dev, "CE%d window (%dMB) too small for mapping", > chip->cs, chip->ahb_window_size >> 20); > -- > 2.34.1 >