diff options
Diffstat (limited to 'drivers/mtd/spi-nor/controllers/aspeed-smc.c')
-rw-r--r-- | drivers/mtd/spi-nor/controllers/aspeed-smc.c | 588 |
1 files changed, 541 insertions, 47 deletions
diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c index 7225870e8b18..a591ce1d24c5 100644 --- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c +++ b/drivers/mtd/spi-nor/controllers/aspeed-smc.c @@ -6,6 +6,7 @@ */ #include <linux/bug.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> @@ -16,10 +17,13 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/sizes.h> +#include <linux/slab.h> #include <linux/sysfs.h> #define DEVICE_NAME "aspeed-smc" +#define SNOR_F_4B_OPCODES BIT(6) + /* * The driver only support SPI flash */ @@ -30,6 +34,7 @@ enum aspeed_smc_flash_type { }; struct aspeed_smc_chip; +struct aspeed_smc_controller; struct aspeed_smc_info { u32 maxsize; /* maximum size of chip window */ @@ -37,12 +42,34 @@ struct aspeed_smc_info { bool hastype; /* flash type field exists in config reg */ u8 we0; /* shift for write enable bit for CE0 */ u8 ctl0; /* offset in regs of ctl for CE0 */ + u8 timing; /* offset in regs of timing */ + u32 hclk_mask; /* clock frequency mask in CEx Control reg */ + u32 hdiv_max; /* Max HCLK divisor on read timing reg */ void (*set_4b)(struct aspeed_smc_chip *chip); + int (*optimize_read)(struct aspeed_smc_chip *chip, u32 max_freq); + int (*calibrate)(struct aspeed_smc_chip *chip, u32 hdiv, + const u8 *golden_buf, u8 *test_buf); + + u32 (*segment_start)(struct aspeed_smc_controller *controller, u32 reg); + u32 (*segment_end)(struct aspeed_smc_controller *controller, u32 reg); + u32 (*segment_reg)(struct aspeed_smc_controller *controller, + u32 start, u32 end); }; static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip); static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip); +static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip, + u32 max_freq); +static int aspeed_smc_calibrate_reads(struct aspeed_smc_chip *chip, u32 hdiv, + const u8 *golden_buf, u8 *test_buf); + +static u32 aspeed_smc_segment_start( + struct aspeed_smc_controller *controller, u32 reg); +static u32 aspeed_smc_segment_end( + struct aspeed_smc_controller *controller, u32 reg); +static u32 aspeed_smc_segment_reg( + struct aspeed_smc_controller *controller, u32 start, u32 end); static const struct aspeed_smc_info fmc_2400_info = { .maxsize = 64 * 1024 * 1024, @@ -50,7 +77,15 @@ static const struct aspeed_smc_info fmc_2400_info = { .hastype = true, .we0 = 16, .ctl0 = 0x10, + .timing = 0x94, + .hclk_mask = 0xfffff0ff, + .hdiv_max = 1, .set_4b = aspeed_smc_chip_set_4b, + .optimize_read = aspeed_smc_optimize_read, + .calibrate = aspeed_smc_calibrate_reads, + .segment_start = aspeed_smc_segment_start, + .segment_end = aspeed_smc_segment_end, + .segment_reg = aspeed_smc_segment_reg, }; static const struct aspeed_smc_info spi_2400_info = { @@ -59,7 +94,13 @@ static const struct aspeed_smc_info spi_2400_info = { .hastype = false, .we0 = 0, .ctl0 = 0x04, + .timing = 0x14, + .hclk_mask = 0xfffff0ff, + .hdiv_max = 1, .set_4b = aspeed_smc_chip_set_4b_spi_2400, + .optimize_read = aspeed_smc_optimize_read, + .calibrate = aspeed_smc_calibrate_reads, + /* No segment registers */ }; static const struct aspeed_smc_info fmc_2500_info = { @@ -68,7 +109,15 @@ static const struct aspeed_smc_info fmc_2500_info = { .hastype = true, .we0 = 16, .ctl0 = 0x10, + .timing = 0x94, + .hclk_mask = 0xfffff0ff, + .hdiv_max = 1, .set_4b = aspeed_smc_chip_set_4b, + .optimize_read = aspeed_smc_optimize_read, + .calibrate = aspeed_smc_calibrate_reads, + .segment_start = aspeed_smc_segment_start, + .segment_end = aspeed_smc_segment_end, + .segment_reg = aspeed_smc_segment_reg, }; static const struct aspeed_smc_info spi_2500_info = { @@ -77,7 +126,59 @@ static const struct aspeed_smc_info spi_2500_info = { .hastype = false, .we0 = 16, .ctl0 = 0x10, + .timing = 0x94, + .hclk_mask = 0xfffff0ff, + .hdiv_max = 1, + .set_4b = aspeed_smc_chip_set_4b, + .optimize_read = aspeed_smc_optimize_read, + .calibrate = aspeed_smc_calibrate_reads, + .segment_start = aspeed_smc_segment_start, + .segment_end = aspeed_smc_segment_end, + .segment_reg = aspeed_smc_segment_reg, +}; + +static u32 aspeed_smc_segment_start_ast2600( + struct aspeed_smc_controller *controller, u32 reg); +static u32 aspeed_smc_segment_end_ast2600( + struct aspeed_smc_controller *controller, u32 reg); +static u32 aspeed_smc_segment_reg_ast2600( + struct aspeed_smc_controller *controller, u32 start, u32 end); + +static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip *chip, + u32 hdiv, const u8 *golden_buf, u8 *test_buf); + +static const struct aspeed_smc_info fmc_2600_info = { + .maxsize = 256 * 1024 * 1024, + .nce = 3, + .hastype = false, /* SPI Only */ + .we0 = 16, + .ctl0 = 0x10, + .timing = 0x94, + .hclk_mask = 0xf0fff0ff, + .hdiv_max = 2, + .set_4b = aspeed_smc_chip_set_4b, + .optimize_read = aspeed_smc_optimize_read, + .calibrate = aspeed_smc_calibrate_reads_ast2600, + .segment_start = aspeed_smc_segment_start_ast2600, + .segment_end = aspeed_smc_segment_end_ast2600, + .segment_reg = aspeed_smc_segment_reg_ast2600, +}; + +static const struct aspeed_smc_info spi_2600_info = { + .maxsize = 256 * 1024 * 1024, + .nce = 2, + .hastype = false, + .we0 = 16, + .ctl0 = 0x10, + .timing = 0x94, + .hclk_mask = 0xf0fff0ff, + .hdiv_max = 2, .set_4b = aspeed_smc_chip_set_4b, + .optimize_read = aspeed_smc_optimize_read, + .calibrate = aspeed_smc_calibrate_reads_ast2600, + .segment_start = aspeed_smc_segment_start_ast2600, + .segment_end = aspeed_smc_segment_end_ast2600, + .segment_reg = aspeed_smc_segment_reg_ast2600, }; enum aspeed_smc_ctl_reg_value { @@ -98,6 +199,7 @@ struct aspeed_smc_chip { u32 ctl_val[smc_max]; /* control settings */ enum aspeed_smc_flash_type type; /* what type of flash */ struct spi_nor nor; + u32 clk_rate; }; struct aspeed_smc_controller { @@ -106,12 +208,17 @@ struct aspeed_smc_controller { struct mutex mutex; /* controller access mutex */ const struct aspeed_smc_info *info; /* type info of controller */ void __iomem *regs; /* controller registers */ - void __iomem *ahb_base; /* per-chip windows resource */ + void __iomem *ahb_base; /* per-chip window resource */ + u32 ahb_base_phy; /* phys addr of AHB window */ u32 ahb_window_size; /* full mapping window size */ + unsigned long clk_frequency; + struct aspeed_smc_chip *chips[]; /* pointers to attached chips */ }; +#define ASPEED_SPI_DEFAULT_FREQ 50000000 + /* * SPI Flash Configuration Register (AST2500 SPI) * or @@ -181,23 +288,82 @@ struct aspeed_smc_controller { (CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \ CONTROL_CLOCK_FREQ_SEL_MASK | CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3) -/* - * The Segment Register uses a 8MB unit to encode the start address - * and the end address of the mapping window of a flash SPI slave : - * - * | byte 1 | byte 2 | byte 3 | byte 4 | - * +--------+--------+--------+--------+ - * | end | start | 0 | 0 | - */ #define SEGMENT_ADDR_REG0 0x30 -#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23) -#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23) -#define SEGMENT_ADDR_VALUE(start, end) \ - (((((start) >> 23) & 0xFF) << 16) | ((((end) >> 23) & 0xFF) << 24)) #define SEGMENT_ADDR_REG(controller, cs) \ ((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4) /* + * The Segment Registers of the AST2400 and AST2500 have a 8MB + * unit. The address range of a flash SPI slave is encoded with + * absolute addresses which should be part of the overall controller + * window. + */ +static u32 aspeed_smc_segment_start( + struct aspeed_smc_controller *controller, u32 reg) +{ + return ((reg >> 16) & 0xFF) << 23; +} + +static u32 aspeed_smc_segment_end( + struct aspeed_smc_controller *controller, u32 reg) +{ + return ((reg >> 24) & 0xFF) << 23; +} + +static u32 aspeed_smc_segment_reg( + struct aspeed_smc_controller *controller, u32 start, u32 end) +{ + return (((start >> 23) & 0xFF) << 16) | (((end >> 23) & 0xFF) << 24); +} + +/* + * The Segment Registers of the AST2600 have a 1MB unit. The address + * range of a flash SPI slave is encoded with offsets in the overall + * controller window. The previous SoC AST2400 and AST2500 used + * absolute addresses. Only bits [27:20] are relevant and the end + * address is an upper bound limit. + */ + +#define AST2600_SEG_ADDR_MASK 0x0ff00000 + +static u32 aspeed_smc_segment_start_ast2600( + struct aspeed_smc_controller *controller, u32 reg) +{ + uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; + + return controller->ahb_base_phy + start_offset; +} + +static u32 aspeed_smc_segment_end_ast2600( + struct aspeed_smc_controller *controller, u32 reg) +{ + uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK; + + /* segment is disabled */ + if (!end_offset) + return controller->ahb_base_phy; + + return controller->ahb_base_phy + end_offset + 0x100000; +} + +static u32 aspeed_smc_segment_reg_ast2600( + struct aspeed_smc_controller *controller, u32 start, u32 end) +{ + /* disable zero size segments */ + if (start == end) + return 0; + + return ((start & AST2600_SEG_ADDR_MASK) >> 16) | + ((end - 1) & AST2600_SEG_ADDR_MASK); +} + +/* + * Switch to turn off read optimisation if needed + */ +static bool optimize_read = true; +module_param(optimize_read, bool, 0644); + +/* * In user mode all data bytes read or written to the chip decode address * range are transferred to or from the SPI bus. The range is treated as a * fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned @@ -370,18 +536,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; @@ -399,6 +596,31 @@ static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to, return len; } +static ssize_t aspeed_smc_read(struct spi_nor *nor, loff_t from, size_t len, + u_char *read_buf) +{ + struct aspeed_smc_chip *chip = nor->priv; + + /* + * The AHB window configured for the chip is too small for the + * read offset. Use the "User mode" of the controller to + * perform the read. + */ + if (from >= chip->ahb_window_size) { + aspeed_smc_read_user(nor, from, len, read_buf); + goto out; + } + + /* + * Use the "Command mode" to do a direct read from the AHB + * window configured for the chip. This should be the default. + */ + memcpy_fromio(read_buf, chip->ahb_base + from, len); + +out: + return len; +} + static int aspeed_smc_unregister(struct aspeed_smc_controller *controller) { struct aspeed_smc_chip *chip; @@ -423,6 +645,8 @@ static const struct of_device_id aspeed_smc_matches[] = { { .compatible = "aspeed,ast2400-spi", .data = &spi_2400_info }, { .compatible = "aspeed,ast2500-fmc", .data = &fmc_2500_info }, { .compatible = "aspeed,ast2500-spi", .data = &spi_2500_info }, + { .compatible = "aspeed,ast2600-fmc", .data = &fmc_2600_info }, + { .compatible = "aspeed,ast2600-spi", .data = &spi_2600_info }, { } }; MODULE_DEVICE_TABLE(of, aspeed_smc_matches); @@ -438,36 +662,32 @@ static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip, struct resource *res) { struct aspeed_smc_controller *controller = chip->controller; + const struct aspeed_smc_info *info = controller->info; u32 offset = 0; u32 reg; - if (controller->info->nce > 1) { + if (info->nce > 1) { reg = readl(SEGMENT_ADDR_REG(controller, chip->cs)); - if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg)) + if (info->segment_start(controller, reg) >= + info->segment_end(controller, reg)) { return NULL; + } - offset = SEGMENT_ADDR_START(reg) - res->start; + offset = info->segment_start(controller, reg) - res->start; } return controller->ahb_base + offset; } -static u32 aspeed_smc_ahb_base_phy(struct aspeed_smc_controller *controller) -{ - u32 seg0_val = readl(SEGMENT_ADDR_REG(controller, 0)); - - return SEGMENT_ADDR_START(seg0_val); -} - static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start, u32 size) { struct aspeed_smc_controller *controller = chip->controller; + const struct aspeed_smc_info *info = controller->info; void __iomem *seg_reg; - u32 seg_oldval, seg_newval, ahb_base_phy, end; - - ahb_base_phy = aspeed_smc_ahb_base_phy(controller); + u32 seg_oldval, seg_newval, end; + u32 ahb_base_phy = controller->ahb_base_phy; seg_reg = SEGMENT_ADDR_REG(controller, cs); seg_oldval = readl(seg_reg); @@ -477,8 +697,15 @@ static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start, * size, but take into account the possible overlap with the * previous segment */ - if (!size) - size = SEGMENT_ADDR_END(seg_oldval) - start; + if (!size) { + end = info->segment_end(controller, seg_oldval); + + /* + * Check for disabled segment (AST2600). + */ + if (end != ahb_base_phy) + size = end - start; + } /* * The segment cannot exceed the maximum window size of the @@ -491,7 +718,7 @@ static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start, } end = start + size; - seg_newval = SEGMENT_ADDR_VALUE(start, end); + seg_newval = info->segment_reg(controller, start, end); writel(seg_newval, seg_reg); /* @@ -502,13 +729,13 @@ static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start, if (seg_newval != readl(seg_reg)) { dev_err(chip->nor.dev, "CE%d window invalid", cs); writel(seg_oldval, seg_reg); - start = SEGMENT_ADDR_START(seg_oldval); - end = SEGMENT_ADDR_END(seg_oldval); + start = info->segment_start(controller, seg_oldval); + end = info->segment_end(controller, seg_oldval); size = end - start; } - dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB", - cs, start, end, size >> 20); + dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB%s", + cs, start, end, size >> 20, size ? "" : " (disabled)"); return size; } @@ -556,7 +783,7 @@ static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip) chip->cs, size >> 20); } - ahb_base_phy = aspeed_smc_ahb_base_phy(controller); + ahb_base_phy = controller->ahb_base_phy; /* * As a start address for the current segment, use the default @@ -566,7 +793,7 @@ static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip) if (chip->cs) { u32 prev = readl(SEGMENT_ADDR_REG(controller, chip->cs - 1)); - start = SEGMENT_ADDR_END(prev); + start = controller->info->segment_end(controller, prev); } else { start = ahb_base_phy; } @@ -703,10 +930,258 @@ static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip, return 0; } + +#define CALIBRATE_BUF_SIZE 16384 + +static bool aspeed_smc_check_reads(struct aspeed_smc_chip *chip, + const u8 *golden_buf, u8 *test_buf) +{ + int i; + + for (i = 0; i < 10; i++) { + memcpy_fromio(test_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); + if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0) + return false; + } + return true; +} + +static int aspeed_smc_calibrate_reads(struct aspeed_smc_chip *chip, u32 hdiv, + const u8 *golden_buf, u8 *test_buf) +{ + struct aspeed_smc_controller *controller = chip->controller; + const struct aspeed_smc_info *info = controller->info; + int i; + int good_pass = -1, pass_count = 0; + u32 shift = (hdiv - 1) << 2; + u32 mask = ~(0xfu << shift); + u32 fread_timing_val = 0; + +#define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8)) + + /* Try HCLK delay 0..5, each one with/without delay and look for a + * good pair. + */ + for (i = 0; i < 12; i++) { + bool pass; + + fread_timing_val &= mask; + fread_timing_val |= FREAD_TPASS(i) << shift; + + writel(fread_timing_val, controller->regs + info->timing); + pass = aspeed_smc_check_reads(chip, golden_buf, test_buf); + dev_dbg(chip->nor.dev, + " * [%08x] %d HCLK delay, %dns DI delay : %s", + fread_timing_val, i/2, (i & 1) ? 0 : 4, + pass ? "PASS" : "FAIL"); + if (pass) { + pass_count++; + if (pass_count == 3) { + good_pass = i - 1; + break; + } + } else + pass_count = 0; + } + + /* No good setting for this frequency */ + if (good_pass < 0) + return -1; + + /* We have at least one pass of margin, let's use first pass */ + fread_timing_val &= mask; + fread_timing_val |= FREAD_TPASS(good_pass) << shift; + writel(fread_timing_val, controller->regs + info->timing); + dev_dbg(chip->nor.dev, " * -> good is pass %d [0x%08x]", + good_pass, fread_timing_val); + return 0; +} + +static bool aspeed_smc_check_calib_data(const u8 *test_buf, u32 size) +{ + const u32 *tb32 = (const u32 *) test_buf; + u32 i, cnt = 0; + + /* We check if we have enough words that are neither all 0 + * nor all 1's so the calibration can be considered valid. + * + * I use an arbitrary threshold for now of 64 + */ + size >>= 2; + for (i = 0; i < size; i++) { + if (tb32[i] != 0 && tb32[i] != 0xffffffff) + cnt++; + } + return cnt >= 64; +} + +static const uint32_t aspeed_smc_hclk_divs[] = { + 0xf, /* HCLK */ + 0x7, /* HCLK/2 */ + 0xe, /* HCLK/3 */ + 0x6, /* HCLK/4 */ + 0xd, /* HCLK/5 */ +}; +#define ASPEED_SMC_HCLK_DIV(i) \ + (aspeed_smc_hclk_divs[(i) - 1] << CONTROL_CLOCK_FREQ_SEL_SHIFT) + +static u32 aspeed_smc_default_read(struct aspeed_smc_chip *chip) +{ + /* + * Keep the 4Byte address mode on the AST2400 SPI controller. + * Other controllers set the 4Byte mode in the CE Control + * Register + */ + u32 ctl_mask = chip->controller->info == &spi_2400_info ? + CONTROL_IO_ADDRESS_4B : 0; + u8 cmd = chip->nor.addr_width == 4 ? SPINOR_OP_READ_4B : + SPINOR_OP_READ; + + /* + * Use the "read command" mode to customize the opcode. In + * normal command mode, the value is necessarily READ (0x3) on + * the AST2400/2500 SoCs. + */ + return (chip->ctl_val[smc_read] & ctl_mask) | + (0x00 << 28) | /* Single bit */ + (0x00 << 24) | /* CE# max */ + (cmd << 16) | /* use read mode to support 4B opcode */ + (0x00 << 8) | /* HCLK/16 */ + (0x00 << 6) | /* no dummy cycle */ + (0x01); /* read mode */ +} + +static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip, + u32 max_freq) +{ + struct aspeed_smc_controller *controller = chip->controller; + const struct aspeed_smc_info *info = controller->info; + u8 *golden_buf, *test_buf; + int i, rc, best_div = -1; + u32 save_read_val = chip->ctl_val[smc_read]; + u32 ahb_freq = chip->controller->clk_frequency; + + dev_dbg(chip->nor.dev, "AHB frequency: %d MHz", ahb_freq / 1000000); + + test_buf = kmalloc(CALIBRATE_BUF_SIZE * 2, GFP_KERNEL); + golden_buf = test_buf + CALIBRATE_BUF_SIZE; + + /* We start with the dumbest setting (keep 4Byte bit) and read + * some data + */ + chip->ctl_val[smc_read] = aspeed_smc_default_read(chip); + + writel(chip->ctl_val[smc_read], chip->ctl); + + memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); + + /* Establish our read mode with freq field set to 0 (HCLK/16) */ + chip->ctl_val[smc_read] = save_read_val & info->hclk_mask; + + /* Check if calibration data is suitable */ + if (!aspeed_smc_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) { + dev_info(chip->nor.dev, + "Calibration area too uniform, using low speed"); + writel(chip->ctl_val[smc_read], chip->ctl); + kfree(test_buf); + return 0; + } + + /* Now we iterate the HCLK dividers until we find our breaking point */ + for (i = ARRAY_SIZE(aspeed_smc_hclk_divs); i > info->hdiv_max - 1; i--) { + u32 tv, freq; + + /* Compare timing to max */ + freq = ahb_freq / i; + if (freq > max_freq) + continue; + + /* Set the timing */ + tv = chip->ctl_val[smc_read] | ASPEED_SMC_HCLK_DIV(i); + writel(tv, chip->ctl); + dev_dbg(chip->nor.dev, "Trying HCLK/%d [%08x] ...", i, tv); + rc = info->calibrate(chip, i, golden_buf, test_buf); + if (rc == 0) + best_div = i; + } + kfree(test_buf); + + /* Nothing found ? */ + if (best_div < 0) + dev_warn(chip->nor.dev, "No good frequency, using dumb slow"); + else { + dev_dbg(chip->nor.dev, "Found good read timings at HCLK/%d", + best_div); + chip->ctl_val[smc_read] |= ASPEED_SMC_HCLK_DIV(best_div); + } + + writel(chip->ctl_val[smc_read], chip->ctl); + return 0; +} + +#define TIMING_DELAY_DI BIT(3) +#define TIMING_DELAY_HCYCLE_MAX 5 +#define TIMING_REG_AST2600(chip) \ + ((chip)->controller->regs + (chip)->controller->info->timing + \ + (chip)->cs * 4) + +static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip *chip, u32 hdiv, + const u8 *golden_buf, u8 *test_buf) +{ + int hcycle; + u32 shift = (hdiv - 2) << 3; + u32 mask = ~(0xfu << shift); + u32 fread_timing_val = 0; + + for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) { + int delay_ns; + bool pass = false; + + fread_timing_val &= mask; + fread_timing_val |= hcycle << shift; + + /* no DI input delay first */ + writel(fread_timing_val, TIMING_REG_AST2600(chip)); + pass = aspeed_smc_check_reads(chip, golden_buf, test_buf); + dev_dbg(chip->nor.dev, + " * [%08x] %d HCLK delay, DI delay none : %s", + fread_timing_val, hcycle, pass ? "PASS" : "FAIL"); + if (pass) + return 0; + + /* Add DI input delays */ + fread_timing_val &= mask; + fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift; + + for (delay_ns = 0; delay_ns < 0x10; delay_ns++) { + fread_timing_val &= ~(0xf << (4 + shift)); + fread_timing_val |= delay_ns << (4 + shift); + + writel(fread_timing_val, TIMING_REG_AST2600(chip)); + pass = aspeed_smc_check_reads(chip, golden_buf, test_buf); + dev_dbg(chip->nor.dev, + " * [%08x] %d HCLK delay, DI delay %d.%dns : %s", + fread_timing_val, hcycle, (delay_ns + 1)/2, + (delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL"); + /* + * TODO: This is optimistic. We should look + * for a working interval and save the middle + * value in the read timing register. + */ + if (pass) + return 0; + } + } + + /* No good setting for this frequency */ + return -1; +} + 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) @@ -729,21 +1204,24 @@ 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]); + + if (optimize_read && info->optimize_read) + info->optimize_read(chip, chip->clk_rate); return 0; } @@ -752,7 +1230,7 @@ static const struct spi_nor_controller_ops aspeed_smc_controller_ops = { .unprepare = aspeed_smc_unprep, .read_reg = aspeed_smc_read_reg, .write_reg = aspeed_smc_write_reg, - .read = aspeed_smc_read_user, + .read = aspeed_smc_read, .write = aspeed_smc_write_user, }; @@ -762,6 +1240,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; @@ -805,6 +1284,13 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, break; } + if (of_property_read_u32(child, "spi-max-frequency", + &chip->clk_rate)) { + chip->clk_rate = ASPEED_SPI_DEFAULT_FREQ; + } + dev_info(dev, "Using %d MHz SPI frequency\n", + chip->clk_rate / 1000000); + chip->controller = controller; chip->ctl = controller->regs + info->ctl0 + cs * 4; chip->cs = cs; @@ -856,6 +1342,7 @@ static int aspeed_smc_probe(struct platform_device *pdev) struct aspeed_smc_controller *controller; const struct of_device_id *match; const struct aspeed_smc_info *info; + struct clk *clk; struct resource *res; int ret; @@ -881,12 +1368,19 @@ static int aspeed_smc_probe(struct platform_device *pdev) return PTR_ERR(controller->regs); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + controller->ahb_base_phy = res->start; controller->ahb_base = devm_ioremap_resource(dev, res); if (IS_ERR(controller->ahb_base)) return PTR_ERR(controller->ahb_base); controller->ahb_window_size = resource_size(res); + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + controller->clk_frequency = clk_get_rate(clk); + devm_clk_put(&pdev->dev, clk); + ret = aspeed_smc_setup_flash(controller, np, res); if (ret) dev_err(dev, "Aspeed SMC probe failed %d\n", ret); |