diff options
author | Cédric Le Goater <clg@kaod.org> | 2017-10-09 16:41:05 +0300 |
---|---|---|
committer | Joel Stanley <joel@jms.id.au> | 2021-11-01 09:42:39 +0300 |
commit | 485d400164f2595bf95ba781b87d7beeeee10cc4 (patch) | |
tree | c15c524f807327c6b36199f669aa8e2da969654f | |
parent | 1a6c61944f7832b91cb1edab11bd4a0eb73fcc47 (diff) | |
download | linux-485d400164f2595bf95ba781b87d7beeeee10cc4.tar.xz |
mtd: spi-nor: aspeed: add support for SPI dual IO read mode
Implements support for the dual IO read mode on aspeed SMC/FMC
controllers which uses both MISO and MOSI lines for data during a read
to double the read bandwidth.
Still to be done SNOR_PROTO_1_2_2
Based on work from Robert Lippert <roblip@gmail.com>
OpenBMC-Staging-Count: 14
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
-rw-r--r-- | drivers/mtd/spi-nor/controllers/aspeed-smc.c | 54 |
1 files changed, 43 insertions, 11 deletions
diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c index 92ec91f4bded..ee5715238253 100644 --- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c +++ b/drivers/mtd/spi-nor/controllers/aspeed-smc.c @@ -370,18 +370,49 @@ static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr) } } +static int aspeed_smc_get_io_mode(struct aspeed_smc_chip *chip) +{ + switch (chip->nor.read_proto) { + case SNOR_PROTO_1_1_1: + return 0; + case SNOR_PROTO_1_1_2: + return CONTROL_IO_DUAL_DATA; + case SNOR_PROTO_1_2_2: + return CONTROL_IO_DUAL_ADDR_DATA; + default: + dev_err(chip->nor.dev, "unsupported SPI read mode\n"); + return -EINVAL; + } +} + +static void aspeed_smc_set_io_mode(struct aspeed_smc_chip *chip, u32 io_mode) +{ + u32 ctl; + + if (io_mode > 0) { + ctl = readl(chip->ctl) & ~CONTROL_IO_MODE_MASK; + ctl |= io_mode; + writel(ctl, chip->ctl); + } +} + static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from, size_t len, u_char *read_buf) { struct aspeed_smc_chip *chip = nor->priv; int i; u8 dummy = 0xFF; + int io_mode = aspeed_smc_get_io_mode(chip); aspeed_smc_start_user(nor); aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from); for (i = 0; i < chip->nor.read_dummy / 8; i++) aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy)); + /* Set IO mode only for data */ + if (io_mode == CONTROL_IO_DUAL_DATA) + aspeed_smc_set_io_mode(chip, io_mode); + aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len); aspeed_smc_stop_user(nor); return len; @@ -732,6 +763,7 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) { struct aspeed_smc_controller *controller = chip->controller; const struct aspeed_smc_info *info = controller->info; + int io_mode; u32 cmd; if (chip->nor.addr_width == 4 && info->set_4b) @@ -754,21 +786,20 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) * TODO: Adjust clocks if fast read is supported and interpret * SPI NOR flags to adjust controller settings. */ - if (chip->nor.read_proto == SNOR_PROTO_1_1_1) { - if (chip->nor.read_dummy == 0) - cmd = CONTROL_COMMAND_MODE_NORMAL; - else - cmd = CONTROL_COMMAND_MODE_FREAD; - } else { - dev_err(chip->nor.dev, "unsupported SPI read mode\n"); - return -EINVAL; - } + io_mode = aspeed_smc_get_io_mode(chip); + if (io_mode < 0) + return io_mode; + + if (chip->nor.read_dummy == 0) + cmd = CONTROL_COMMAND_MODE_NORMAL; + else + cmd = CONTROL_COMMAND_MODE_FREAD; - chip->ctl_val[smc_read] |= cmd | + chip->ctl_val[smc_read] |= cmd | io_mode | chip->nor.read_opcode << CONTROL_COMMAND_SHIFT | CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8); - dev_dbg(controller->dev, "base control register: %08x\n", + dev_info(controller->dev, "read control register: %08x\n", chip->ctl_val[smc_read]); return 0; } @@ -788,6 +819,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | + SNOR_HWCAPS_READ_1_1_2 | SNOR_HWCAPS_PP, }; const struct aspeed_smc_info *info = controller->info; |