summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-aspeed-smc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-aspeed-smc.c')
-rw-r--r--drivers/spi/spi-aspeed-smc.c156
1 files changed, 58 insertions, 98 deletions
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 29fed8477958..83a47ac0711e 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -379,61 +379,59 @@ static const char *aspeed_spi_get_name(struct spi_mem *mem)
spi_get_chipselect(mem->spi, 0));
}
-struct aspeed_spi_window {
- u32 cs;
- u32 offset;
- u32 size;
-};
-
-static void aspeed_spi_get_windows(struct aspeed_spi *aspi,
- struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS])
+static int aspeed_spi_set_window(struct aspeed_spi *aspi)
{
- const struct aspeed_spi_data *data = aspi->data;
- u32 reg_val;
+ struct device *dev = aspi->dev;
+ off_t offset = 0;
+ phys_addr_t start;
+ phys_addr_t end;
+ void __iomem *seg_reg_base = aspi->regs + CE0_SEGMENT_ADDR_REG;
+ void __iomem *seg_reg;
+ u32 seg_val_backup;
+ u32 seg_val;
u32 cs;
+ size_t window_size;
for (cs = 0; cs < aspi->data->max_cs; cs++) {
- reg_val = readl(aspi->regs + CE0_SEGMENT_ADDR_REG + cs * 4);
- windows[cs].cs = cs;
- windows[cs].size = data->segment_end(aspi, reg_val) -
- data->segment_start(aspi, reg_val);
- windows[cs].offset = data->segment_start(aspi, reg_val) - aspi->ahb_base_phy;
- dev_vdbg(aspi->dev, "CE%d offset=0x%.8x size=0x%x\n", cs,
- windows[cs].offset, windows[cs].size);
- }
-}
+ seg_reg = seg_reg_base + cs * 4;
+ seg_val_backup = readl(seg_reg);
-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);
+ start = aspi->ahb_base_phy + offset;
+ window_size = aspi->chips[cs].ahb_window_size;
+ end = start + window_size;
- if (seg_val == seg_val_backup)
- return 0;
+ seg_val = aspi->data->segment_reg(aspi, start, end);
+ writel(seg_val, seg_reg);
- writel(seg_val, seg_reg);
+ /*
+ * Restore initial value if something goes wrong or the segment
+ * register is written protected.
+ */
+ if (seg_val != readl(seg_reg)) {
+ dev_warn(dev, "CE%d expected window [ 0x%.9llx - 0x%.9llx ] %zdMB\n",
+ cs, (u64)start, (u64)end - 1, window_size >> 20);
+ writel(seg_val_backup, seg_reg);
+ window_size = aspi->data->segment_end(aspi, seg_val_backup) -
+ aspi->data->segment_start(aspi, seg_val_backup);
+ aspi->chips[cs].ahb_window_size = window_size;
+ end = start + window_size;
+ }
- /*
- * 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 (window_size != 0)
+ dev_dbg(dev, "CE%d window [ 0x%.9llx - 0x%.9llx ] %zdMB\n",
+ cs, (u64)start, (u64)end - 1, window_size >> 20);
+ else
+ dev_dbg(dev, "CE%d window closed\n", cs);
+
+ aspi->chips[cs].ahb_base = aspi->ahb_base + offset;
+ offset += window_size;
+ if (offset > aspi->ahb_window_size) {
+ dev_err(dev, "CE%d offset value 0x%llx is too large.\n",
+ cs, (u64)offset);
+ return -ENOSPC;
+ }
}
- 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;
}
@@ -443,9 +441,7 @@ static const struct aspeed_spi_data ast2600_fmc_data;
static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi)
{
- int ret;
u32 cs;
- struct aspeed_spi_window win;
/* No segment registers for the AST2400 SPI controller */
if (aspi->data == &ast2400_spi_data) {
@@ -456,36 +452,17 @@ static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi)
/* Assign the minimum window size to each CS */
for (cs = 0; cs < aspi->num_cs; cs++) {
- if (cs == 0)
- aspi->chips[cs].ahb_base = aspi->ahb_base;
- else
- aspi->chips[cs].ahb_base =
- aspi->chips[cs - 1].ahb_base +
- aspi->chips[cs - 1].ahb_window_size;
-
aspi->chips[cs].ahb_window_size = aspi->data->min_window_size;
-
dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ]",
cs, aspi->ahb_base_phy + aspi->data->min_window_size * cs,
aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1);
}
/* Close unused CS */
- for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) {
- aspi->chips[cs].ahb_base = aspi->ahb_base;
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
aspi->chips[cs].ahb_window_size = 0;
- }
-
- for (cs = 0; cs < aspi->num_cs; cs++) {
- win.cs = cs;
- win.offset = aspi->chips[cs].ahb_base - aspi->ahb_base;
- win.size = aspi->chips[cs].ahb_window_size;
- ret = aspeed_spi_set_window(aspi, &win);
- if (ret)
- return ret;
- }
- return 0;
+ return aspeed_spi_set_window(aspi);
}
/*
@@ -498,8 +475,8 @@ 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];
+ u32 cs;
+ u32 total_window_size;
int ret;
/* No segment registers for the AST2400 SPI controller */
@@ -527,41 +504,24 @@ static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip,
chip->cs, size >> 20);
}
- aspeed_spi_get_windows(aspi, windows);
-
/* Adjust this chip window */
- win->offset += local_offset;
- win->size = size;
+ aspi->chips[chip->cs].ahb_window_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);
+ total_window_size = 0;
+ for (cs = 0; cs < aspi->data->max_cs; cs++)
+ total_window_size += aspi->chips[cs].ahb_window_size;
+
+ if (total_window_size > aspi->ahb_window_size) {
+ aspi->chips[chip->cs].ahb_window_size -= (total_window_size -
+ aspi->ahb_window_size);
+ dev_warn(aspi->dev, "CE%d window resized to %zdMB",
+ chip->cs, aspi->chips[chip->cs].ahb_window_size >> 20);
}
- ret = aspeed_spi_set_window(aspi, win);
+ ret = aspeed_spi_set_window(aspi);
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;
}