18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ASPEED Static Memory Controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015-2016, IBM Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bug.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mutex.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/spi-nor.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 188c2ecf20Sopenharmony_ci#include <linux/sizes.h> 198c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DEVICE_NAME "aspeed-smc" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * The driver only support SPI flash 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_cienum aspeed_smc_flash_type { 278c2ecf20Sopenharmony_ci smc_type_nor = 0, 288c2ecf20Sopenharmony_ci smc_type_nand = 1, 298c2ecf20Sopenharmony_ci smc_type_spi = 2, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct aspeed_smc_chip; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct aspeed_smc_info { 358c2ecf20Sopenharmony_ci u32 maxsize; /* maximum size of chip window */ 368c2ecf20Sopenharmony_ci u8 nce; /* number of chip enables */ 378c2ecf20Sopenharmony_ci bool hastype; /* flash type field exists in config reg */ 388c2ecf20Sopenharmony_ci u8 we0; /* shift for write enable bit for CE0 */ 398c2ecf20Sopenharmony_ci u8 ctl0; /* offset in regs of ctl for CE0 */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci void (*set_4b)(struct aspeed_smc_chip *chip); 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip); 458c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const struct aspeed_smc_info fmc_2400_info = { 488c2ecf20Sopenharmony_ci .maxsize = 64 * 1024 * 1024, 498c2ecf20Sopenharmony_ci .nce = 5, 508c2ecf20Sopenharmony_ci .hastype = true, 518c2ecf20Sopenharmony_ci .we0 = 16, 528c2ecf20Sopenharmony_ci .ctl0 = 0x10, 538c2ecf20Sopenharmony_ci .set_4b = aspeed_smc_chip_set_4b, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct aspeed_smc_info spi_2400_info = { 578c2ecf20Sopenharmony_ci .maxsize = 64 * 1024 * 1024, 588c2ecf20Sopenharmony_ci .nce = 1, 598c2ecf20Sopenharmony_ci .hastype = false, 608c2ecf20Sopenharmony_ci .we0 = 0, 618c2ecf20Sopenharmony_ci .ctl0 = 0x04, 628c2ecf20Sopenharmony_ci .set_4b = aspeed_smc_chip_set_4b_spi_2400, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic const struct aspeed_smc_info fmc_2500_info = { 668c2ecf20Sopenharmony_ci .maxsize = 256 * 1024 * 1024, 678c2ecf20Sopenharmony_ci .nce = 3, 688c2ecf20Sopenharmony_ci .hastype = true, 698c2ecf20Sopenharmony_ci .we0 = 16, 708c2ecf20Sopenharmony_ci .ctl0 = 0x10, 718c2ecf20Sopenharmony_ci .set_4b = aspeed_smc_chip_set_4b, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic const struct aspeed_smc_info spi_2500_info = { 758c2ecf20Sopenharmony_ci .maxsize = 128 * 1024 * 1024, 768c2ecf20Sopenharmony_ci .nce = 2, 778c2ecf20Sopenharmony_ci .hastype = false, 788c2ecf20Sopenharmony_ci .we0 = 16, 798c2ecf20Sopenharmony_ci .ctl0 = 0x10, 808c2ecf20Sopenharmony_ci .set_4b = aspeed_smc_chip_set_4b, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cienum aspeed_smc_ctl_reg_value { 848c2ecf20Sopenharmony_ci smc_base, /* base value without mode for other commands */ 858c2ecf20Sopenharmony_ci smc_read, /* command reg for (maybe fast) reads */ 868c2ecf20Sopenharmony_ci smc_write, /* command reg for writes */ 878c2ecf20Sopenharmony_ci smc_max, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistruct aspeed_smc_controller; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct aspeed_smc_chip { 938c2ecf20Sopenharmony_ci int cs; 948c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller; 958c2ecf20Sopenharmony_ci void __iomem *ctl; /* control register */ 968c2ecf20Sopenharmony_ci void __iomem *ahb_base; /* base of chip window */ 978c2ecf20Sopenharmony_ci u32 ahb_window_size; /* chip mapping window size */ 988c2ecf20Sopenharmony_ci u32 ctl_val[smc_max]; /* control settings */ 998c2ecf20Sopenharmony_ci enum aspeed_smc_flash_type type; /* what type of flash */ 1008c2ecf20Sopenharmony_ci struct spi_nor nor; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistruct aspeed_smc_controller { 1048c2ecf20Sopenharmony_ci struct device *dev; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci struct mutex mutex; /* controller access mutex */ 1078c2ecf20Sopenharmony_ci const struct aspeed_smc_info *info; /* type info of controller */ 1088c2ecf20Sopenharmony_ci void __iomem *regs; /* controller registers */ 1098c2ecf20Sopenharmony_ci void __iomem *ahb_base; /* per-chip windows resource */ 1108c2ecf20Sopenharmony_ci u32 ahb_window_size; /* full mapping window size */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chips[]; /* pointers to attached chips */ 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * SPI Flash Configuration Register (AST2500 SPI) 1178c2ecf20Sopenharmony_ci * or 1188c2ecf20Sopenharmony_ci * Type setting Register (AST2500 FMC). 1198c2ecf20Sopenharmony_ci * CE0 and CE1 can only be of type SPI. CE2 can be of type NOR but the 1208c2ecf20Sopenharmony_ci * driver does not support it. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci#define CONFIG_REG 0x0 1238c2ecf20Sopenharmony_ci#define CONFIG_DISABLE_LEGACY BIT(31) /* 1 */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define CONFIG_CE2_WRITE BIT(18) 1268c2ecf20Sopenharmony_ci#define CONFIG_CE1_WRITE BIT(17) 1278c2ecf20Sopenharmony_ci#define CONFIG_CE0_WRITE BIT(16) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define CONFIG_CE2_TYPE BIT(4) /* AST2500 FMC only */ 1308c2ecf20Sopenharmony_ci#define CONFIG_CE1_TYPE BIT(2) /* AST2500 FMC only */ 1318c2ecf20Sopenharmony_ci#define CONFIG_CE0_TYPE BIT(0) /* AST2500 FMC only */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * CE Control Register 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci#define CE_CONTROL_REG 0x4 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* 1398c2ecf20Sopenharmony_ci * CEx Control Register 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci#define CONTROL_AAF_MODE BIT(31) 1428c2ecf20Sopenharmony_ci#define CONTROL_IO_MODE_MASK GENMASK(30, 28) 1438c2ecf20Sopenharmony_ci#define CONTROL_IO_DUAL_DATA BIT(29) 1448c2ecf20Sopenharmony_ci#define CONTROL_IO_DUAL_ADDR_DATA (BIT(29) | BIT(28)) 1458c2ecf20Sopenharmony_ci#define CONTROL_IO_QUAD_DATA BIT(30) 1468c2ecf20Sopenharmony_ci#define CONTROL_IO_QUAD_ADDR_DATA (BIT(30) | BIT(28)) 1478c2ecf20Sopenharmony_ci#define CONTROL_CE_INACTIVE_SHIFT 24 1488c2ecf20Sopenharmony_ci#define CONTROL_CE_INACTIVE_MASK GENMASK(27, \ 1498c2ecf20Sopenharmony_ci CONTROL_CE_INACTIVE_SHIFT) 1508c2ecf20Sopenharmony_ci/* 0 = 16T ... 15 = 1T T=HCLK */ 1518c2ecf20Sopenharmony_ci#define CONTROL_COMMAND_SHIFT 16 1528c2ecf20Sopenharmony_ci#define CONTROL_DUMMY_COMMAND_OUT BIT(15) 1538c2ecf20Sopenharmony_ci#define CONTROL_IO_DUMMY_HI BIT(14) 1548c2ecf20Sopenharmony_ci#define CONTROL_IO_DUMMY_HI_SHIFT 14 1558c2ecf20Sopenharmony_ci#define CONTROL_CLK_DIV4 BIT(13) /* others */ 1568c2ecf20Sopenharmony_ci#define CONTROL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI */ 1578c2ecf20Sopenharmony_ci#define CONTROL_RW_MERGE BIT(12) 1588c2ecf20Sopenharmony_ci#define CONTROL_IO_DUMMY_LO_SHIFT 6 1598c2ecf20Sopenharmony_ci#define CONTROL_IO_DUMMY_LO GENMASK(7, \ 1608c2ecf20Sopenharmony_ci CONTROL_IO_DUMMY_LO_SHIFT) 1618c2ecf20Sopenharmony_ci#define CONTROL_IO_DUMMY_MASK (CONTROL_IO_DUMMY_HI | \ 1628c2ecf20Sopenharmony_ci CONTROL_IO_DUMMY_LO) 1638c2ecf20Sopenharmony_ci#define CONTROL_IO_DUMMY_SET(dummy) \ 1648c2ecf20Sopenharmony_ci (((((dummy) >> 2) & 0x1) << CONTROL_IO_DUMMY_HI_SHIFT) | \ 1658c2ecf20Sopenharmony_ci (((dummy) & 0x3) << CONTROL_IO_DUMMY_LO_SHIFT)) 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define CONTROL_CLOCK_FREQ_SEL_SHIFT 8 1688c2ecf20Sopenharmony_ci#define CONTROL_CLOCK_FREQ_SEL_MASK GENMASK(11, \ 1698c2ecf20Sopenharmony_ci CONTROL_CLOCK_FREQ_SEL_SHIFT) 1708c2ecf20Sopenharmony_ci#define CONTROL_LSB_FIRST BIT(5) 1718c2ecf20Sopenharmony_ci#define CONTROL_CLOCK_MODE_3 BIT(4) 1728c2ecf20Sopenharmony_ci#define CONTROL_IN_DUAL_DATA BIT(3) 1738c2ecf20Sopenharmony_ci#define CONTROL_CE_STOP_ACTIVE_CONTROL BIT(2) 1748c2ecf20Sopenharmony_ci#define CONTROL_COMMAND_MODE_MASK GENMASK(1, 0) 1758c2ecf20Sopenharmony_ci#define CONTROL_COMMAND_MODE_NORMAL 0 1768c2ecf20Sopenharmony_ci#define CONTROL_COMMAND_MODE_FREAD 1 1778c2ecf20Sopenharmony_ci#define CONTROL_COMMAND_MODE_WRITE 2 1788c2ecf20Sopenharmony_ci#define CONTROL_COMMAND_MODE_USER 3 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define CONTROL_KEEP_MASK \ 1818c2ecf20Sopenharmony_ci (CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \ 1828c2ecf20Sopenharmony_ci CONTROL_CLOCK_FREQ_SEL_MASK | CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3) 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * The Segment Register uses a 8MB unit to encode the start address 1868c2ecf20Sopenharmony_ci * and the end address of the mapping window of a flash SPI slave : 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * | byte 1 | byte 2 | byte 3 | byte 4 | 1898c2ecf20Sopenharmony_ci * +--------+--------+--------+--------+ 1908c2ecf20Sopenharmony_ci * | end | start | 0 | 0 | 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci#define SEGMENT_ADDR_REG0 0x30 1938c2ecf20Sopenharmony_ci#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23) 1948c2ecf20Sopenharmony_ci#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23) 1958c2ecf20Sopenharmony_ci#define SEGMENT_ADDR_VALUE(start, end) \ 1968c2ecf20Sopenharmony_ci (((((start) >> 23) & 0xFF) << 16) | ((((end) >> 23) & 0xFF) << 24)) 1978c2ecf20Sopenharmony_ci#define SEGMENT_ADDR_REG(controller, cs) \ 1988c2ecf20Sopenharmony_ci ((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * In user mode all data bytes read or written to the chip decode address 2028c2ecf20Sopenharmony_ci * range are transferred to or from the SPI bus. The range is treated as a 2038c2ecf20Sopenharmony_ci * fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned 2048c2ecf20Sopenharmony_ci * to its size. The address within the multiple 8kB range is ignored when 2058c2ecf20Sopenharmony_ci * sending bytes to the SPI bus. 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * On the arm architecture, as of Linux version 4.3, memcpy_fromio and 2088c2ecf20Sopenharmony_ci * memcpy_toio on little endian targets use the optimized memcpy routines 2098c2ecf20Sopenharmony_ci * that were designed for well behavied memory storage. These routines 2108c2ecf20Sopenharmony_ci * have a stutter if the source and destination are not both word aligned, 2118c2ecf20Sopenharmony_ci * once with a duplicate access to the source after aligning to the 2128c2ecf20Sopenharmony_ci * destination to a word boundary, and again with a duplicate access to 2138c2ecf20Sopenharmony_ci * the source when the final byte count is not word aligned. 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * When writing or reading the fifo this stutter discards data or sends 2168c2ecf20Sopenharmony_ci * too much data to the fifo and can not be used by this driver. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * While the low level io string routines that implement the insl family do 2198c2ecf20Sopenharmony_ci * the desired accesses and memory increments, the cross architecture io 2208c2ecf20Sopenharmony_ci * macros make them essentially impossible to use on a memory mapped address 2218c2ecf20Sopenharmony_ci * instead of a a token from the call to iomap of an io port. 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * These fifo routines use readl and friends to a constant io port and update 2248c2ecf20Sopenharmony_ci * the memory buffer pointer and count via explicit code. The final updates 2258c2ecf20Sopenharmony_ci * to len are optimistically suppressed. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic int aspeed_smc_read_from_ahb(void *buf, void __iomem *src, size_t len) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci size_t offset = 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (IS_ALIGNED((uintptr_t)src, sizeof(uintptr_t)) && 2328c2ecf20Sopenharmony_ci IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { 2338c2ecf20Sopenharmony_ci ioread32_rep(src, buf, len >> 2); 2348c2ecf20Sopenharmony_ci offset = len & ~0x3; 2358c2ecf20Sopenharmony_ci len -= offset; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci ioread8_rep(src, (u8 *)buf + offset, len); 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int aspeed_smc_write_to_ahb(void __iomem *dst, const void *buf, 2428c2ecf20Sopenharmony_ci size_t len) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci size_t offset = 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (IS_ALIGNED((uintptr_t)dst, sizeof(uintptr_t)) && 2478c2ecf20Sopenharmony_ci IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { 2488c2ecf20Sopenharmony_ci iowrite32_rep(dst, buf, len >> 2); 2498c2ecf20Sopenharmony_ci offset = len & ~0x3; 2508c2ecf20Sopenharmony_ci len -= offset; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci iowrite8_rep(dst, (const u8 *)buf + offset, len); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic inline u32 aspeed_smc_chip_write_bit(struct aspeed_smc_chip *chip) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci return BIT(chip->controller->info->we0 + chip->cs); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_check_config(struct aspeed_smc_chip *chip) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 2648c2ecf20Sopenharmony_ci u32 reg; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci reg = readl(controller->regs + CONFIG_REG); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (reg & aspeed_smc_chip_write_bit(chip)) 2698c2ecf20Sopenharmony_ci return; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci dev_dbg(controller->dev, "config write is not set ! @%p: 0x%08x\n", 2728c2ecf20Sopenharmony_ci controller->regs + CONFIG_REG, reg); 2738c2ecf20Sopenharmony_ci reg |= aspeed_smc_chip_write_bit(chip); 2748c2ecf20Sopenharmony_ci writel(reg, controller->regs + CONFIG_REG); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void aspeed_smc_start_user(struct spi_nor *nor) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 2808c2ecf20Sopenharmony_ci u32 ctl = chip->ctl_val[smc_base]; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * When the chip is controlled in user mode, we need write 2848c2ecf20Sopenharmony_ci * access to send the opcodes to it. So check the config. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci aspeed_smc_chip_check_config(chip); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ctl |= CONTROL_COMMAND_MODE_USER | 2898c2ecf20Sopenharmony_ci CONTROL_CE_STOP_ACTIVE_CONTROL; 2908c2ecf20Sopenharmony_ci writel(ctl, chip->ctl); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ctl &= ~CONTROL_CE_STOP_ACTIVE_CONTROL; 2938c2ecf20Sopenharmony_ci writel(ctl, chip->ctl); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void aspeed_smc_stop_user(struct spi_nor *nor) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci u32 ctl = chip->ctl_val[smc_read]; 3018c2ecf20Sopenharmony_ci u32 ctl2 = ctl | CONTROL_COMMAND_MODE_USER | 3028c2ecf20Sopenharmony_ci CONTROL_CE_STOP_ACTIVE_CONTROL; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci writel(ctl2, chip->ctl); /* stop user CE control */ 3058c2ecf20Sopenharmony_ci writel(ctl, chip->ctl); /* default to fread or read mode */ 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int aspeed_smc_prep(struct spi_nor *nor) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci mutex_lock(&chip->controller->mutex); 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void aspeed_smc_unprep(struct spi_nor *nor) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci mutex_unlock(&chip->controller->mutex); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, 3248c2ecf20Sopenharmony_ci size_t len) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci aspeed_smc_start_user(nor); 3298c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1); 3308c2ecf20Sopenharmony_ci aspeed_smc_read_from_ahb(buf, chip->ahb_base, len); 3318c2ecf20Sopenharmony_ci aspeed_smc_stop_user(nor); 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, 3368c2ecf20Sopenharmony_ci size_t len) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci aspeed_smc_start_user(nor); 3418c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1); 3428c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, buf, len); 3438c2ecf20Sopenharmony_ci aspeed_smc_stop_user(nor); 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3508c2ecf20Sopenharmony_ci __be32 temp; 3518c2ecf20Sopenharmony_ci u32 cmdaddr; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci switch (nor->addr_width) { 3548c2ecf20Sopenharmony_ci default: 3558c2ecf20Sopenharmony_ci WARN_ONCE(1, "Unexpected address width %u, defaulting to 3\n", 3568c2ecf20Sopenharmony_ci nor->addr_width); 3578c2ecf20Sopenharmony_ci fallthrough; 3588c2ecf20Sopenharmony_ci case 3: 3598c2ecf20Sopenharmony_ci cmdaddr = addr & 0xFFFFFF; 3608c2ecf20Sopenharmony_ci cmdaddr |= cmd << 24; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci temp = cpu_to_be32(cmdaddr); 3638c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4); 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case 4: 3668c2ecf20Sopenharmony_ci temp = cpu_to_be32(addr); 3678c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, &cmd, 1); 3688c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4); 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from, 3748c2ecf20Sopenharmony_ci size_t len, u_char *read_buf) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3778c2ecf20Sopenharmony_ci int i; 3788c2ecf20Sopenharmony_ci u8 dummy = 0xFF; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci aspeed_smc_start_user(nor); 3818c2ecf20Sopenharmony_ci aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from); 3828c2ecf20Sopenharmony_ci for (i = 0; i < chip->nor.read_dummy / 8; i++) 3838c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy)); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len); 3868c2ecf20Sopenharmony_ci aspeed_smc_stop_user(nor); 3878c2ecf20Sopenharmony_ci return len; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to, 3918c2ecf20Sopenharmony_ci size_t len, const u_char *write_buf) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip = nor->priv; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci aspeed_smc_start_user(nor); 3968c2ecf20Sopenharmony_ci aspeed_smc_send_cmd_addr(nor, nor->program_opcode, to); 3978c2ecf20Sopenharmony_ci aspeed_smc_write_to_ahb(chip->ahb_base, write_buf, len); 3988c2ecf20Sopenharmony_ci aspeed_smc_stop_user(nor); 3998c2ecf20Sopenharmony_ci return len; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int aspeed_smc_unregister(struct aspeed_smc_controller *controller) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip; 4058c2ecf20Sopenharmony_ci int n; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (n = 0; n < controller->info->nce; n++) { 4088c2ecf20Sopenharmony_ci chip = controller->chips[n]; 4098c2ecf20Sopenharmony_ci if (chip) 4108c2ecf20Sopenharmony_ci mtd_device_unregister(&chip->nor.mtd); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int aspeed_smc_remove(struct platform_device *dev) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci return aspeed_smc_unregister(platform_get_drvdata(dev)); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const struct of_device_id aspeed_smc_matches[] = { 4228c2ecf20Sopenharmony_ci { .compatible = "aspeed,ast2400-fmc", .data = &fmc_2400_info }, 4238c2ecf20Sopenharmony_ci { .compatible = "aspeed,ast2400-spi", .data = &spi_2400_info }, 4248c2ecf20Sopenharmony_ci { .compatible = "aspeed,ast2500-fmc", .data = &fmc_2500_info }, 4258c2ecf20Sopenharmony_ci { .compatible = "aspeed,ast2500-spi", .data = &spi_2500_info }, 4268c2ecf20Sopenharmony_ci { } 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, aspeed_smc_matches); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* 4318c2ecf20Sopenharmony_ci * Each chip has a mapping window defined by a segment address 4328c2ecf20Sopenharmony_ci * register defining a start and an end address on the AHB bus. These 4338c2ecf20Sopenharmony_ci * addresses can be configured to fit the chip size and offer a 4348c2ecf20Sopenharmony_ci * contiguous memory region across chips. For the moment, we only 4358c2ecf20Sopenharmony_ci * check that each chip segment is valid. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_cistatic void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip, 4388c2ecf20Sopenharmony_ci struct resource *res) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 4418c2ecf20Sopenharmony_ci u32 offset = 0; 4428c2ecf20Sopenharmony_ci u32 reg; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (controller->info->nce > 1) { 4458c2ecf20Sopenharmony_ci reg = readl(SEGMENT_ADDR_REG(controller, chip->cs)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg)) 4488c2ecf20Sopenharmony_ci return NULL; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci offset = SEGMENT_ADDR_START(reg) - res->start; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return controller->ahb_base + offset; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic u32 aspeed_smc_ahb_base_phy(struct aspeed_smc_controller *controller) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci u32 seg0_val = readl(SEGMENT_ADDR_REG(controller, 0)); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return SEGMENT_ADDR_START(seg0_val); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start, 4648c2ecf20Sopenharmony_ci u32 size) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 4678c2ecf20Sopenharmony_ci void __iomem *seg_reg; 4688c2ecf20Sopenharmony_ci u32 seg_oldval, seg_newval, ahb_base_phy, end; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ahb_base_phy = aspeed_smc_ahb_base_phy(controller); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci seg_reg = SEGMENT_ADDR_REG(controller, cs); 4738c2ecf20Sopenharmony_ci seg_oldval = readl(seg_reg); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * If the chip size is not specified, use the default segment 4778c2ecf20Sopenharmony_ci * size, but take into account the possible overlap with the 4788c2ecf20Sopenharmony_ci * previous segment 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci if (!size) 4818c2ecf20Sopenharmony_ci size = SEGMENT_ADDR_END(seg_oldval) - start; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * The segment cannot exceed the maximum window size of the 4858c2ecf20Sopenharmony_ci * controller. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci if (start + size > ahb_base_phy + controller->ahb_window_size) { 4888c2ecf20Sopenharmony_ci size = ahb_base_phy + controller->ahb_window_size - start; 4898c2ecf20Sopenharmony_ci dev_warn(chip->nor.dev, "CE%d window resized to %dMB", 4908c2ecf20Sopenharmony_ci cs, size >> 20); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci end = start + size; 4948c2ecf20Sopenharmony_ci seg_newval = SEGMENT_ADDR_VALUE(start, end); 4958c2ecf20Sopenharmony_ci writel(seg_newval, seg_reg); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * Restore default value if something goes wrong. The chip 4998c2ecf20Sopenharmony_ci * might have set some bogus value and we would loose access 5008c2ecf20Sopenharmony_ci * to the chip. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci if (seg_newval != readl(seg_reg)) { 5038c2ecf20Sopenharmony_ci dev_err(chip->nor.dev, "CE%d window invalid", cs); 5048c2ecf20Sopenharmony_ci writel(seg_oldval, seg_reg); 5058c2ecf20Sopenharmony_ci start = SEGMENT_ADDR_START(seg_oldval); 5068c2ecf20Sopenharmony_ci end = SEGMENT_ADDR_END(seg_oldval); 5078c2ecf20Sopenharmony_ci size = end - start; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB", 5118c2ecf20Sopenharmony_ci cs, start, end, size >> 20); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return size; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* 5178c2ecf20Sopenharmony_ci * The segment register defines the mapping window on the AHB bus and 5188c2ecf20Sopenharmony_ci * it needs to be configured depending on the chip size. The segment 5198c2ecf20Sopenharmony_ci * register of the following CE also needs to be tuned in order to 5208c2ecf20Sopenharmony_ci * provide a contiguous window across multiple chips. 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * This is expected to be called in increasing CE order 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 5278c2ecf20Sopenharmony_ci u32 ahb_base_phy, start; 5288c2ecf20Sopenharmony_ci u32 size = chip->nor.mtd.size; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* 5318c2ecf20Sopenharmony_ci * Each controller has a chip size limit for direct memory 5328c2ecf20Sopenharmony_ci * access 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci if (size > controller->info->maxsize) 5358c2ecf20Sopenharmony_ci size = controller->info->maxsize; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci * The AST2400 SPI controller only handles one chip and does 5398c2ecf20Sopenharmony_ci * not have segment registers. Let's use the chip size for the 5408c2ecf20Sopenharmony_ci * AHB window. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci if (controller->info == &spi_2400_info) 5438c2ecf20Sopenharmony_ci goto out; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * The AST2500 SPI controller has a HW bug when the CE0 chip 5478c2ecf20Sopenharmony_ci * size reaches 128MB. Enforce a size limit of 120MB to 5488c2ecf20Sopenharmony_ci * prevent the controller from using bogus settings in the 5498c2ecf20Sopenharmony_ci * segment register. 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci if (chip->cs == 0 && controller->info == &spi_2500_info && 5528c2ecf20Sopenharmony_ci size == SZ_128M) { 5538c2ecf20Sopenharmony_ci size = 120 << 20; 5548c2ecf20Sopenharmony_ci dev_info(chip->nor.dev, 5558c2ecf20Sopenharmony_ci "CE%d window resized to %dMB (AST2500 HW quirk)", 5568c2ecf20Sopenharmony_ci chip->cs, size >> 20); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ahb_base_phy = aspeed_smc_ahb_base_phy(controller); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * As a start address for the current segment, use the default 5638c2ecf20Sopenharmony_ci * start address if we are handling CE0 or use the previous 5648c2ecf20Sopenharmony_ci * segment ending address 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci if (chip->cs) { 5678c2ecf20Sopenharmony_ci u32 prev = readl(SEGMENT_ADDR_REG(controller, chip->cs - 1)); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci start = SEGMENT_ADDR_END(prev); 5708c2ecf20Sopenharmony_ci } else { 5718c2ecf20Sopenharmony_ci start = ahb_base_phy; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci size = chip_set_segment(chip, chip->cs, start, size); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* Update chip base address on the AHB bus */ 5778c2ecf20Sopenharmony_ci chip->ahb_base = controller->ahb_base + (start - ahb_base_phy); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* 5808c2ecf20Sopenharmony_ci * Now, make sure the next segment does not overlap with the 5818c2ecf20Sopenharmony_ci * current one we just configured, even if there is no 5828c2ecf20Sopenharmony_ci * available chip. That could break access in Command Mode. 5838c2ecf20Sopenharmony_ci */ 5848c2ecf20Sopenharmony_ci if (chip->cs < controller->info->nce - 1) 5858c2ecf20Sopenharmony_ci chip_set_segment(chip, chip->cs + 1, start + size, 0); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ciout: 5888c2ecf20Sopenharmony_ci if (size < chip->nor.mtd.size) 5898c2ecf20Sopenharmony_ci dev_warn(chip->nor.dev, 5908c2ecf20Sopenharmony_ci "CE%d window too small for chip %dMB", 5918c2ecf20Sopenharmony_ci chip->cs, (u32)chip->nor.mtd.size >> 20); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return size; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_enable_write(struct aspeed_smc_chip *chip) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 5998c2ecf20Sopenharmony_ci u32 reg; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci reg = readl(controller->regs + CONFIG_REG); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci reg |= aspeed_smc_chip_write_bit(chip); 6048c2ecf20Sopenharmony_ci writel(reg, controller->regs + CONFIG_REG); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_set_type(struct aspeed_smc_chip *chip, int type) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 6108c2ecf20Sopenharmony_ci u32 reg; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci chip->type = type; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci reg = readl(controller->regs + CONFIG_REG); 6158c2ecf20Sopenharmony_ci reg &= ~(3 << (chip->cs * 2)); 6168c2ecf20Sopenharmony_ci reg |= chip->type << (chip->cs * 2); 6178c2ecf20Sopenharmony_ci writel(reg, controller->regs + CONFIG_REG); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/* 6218c2ecf20Sopenharmony_ci * The first chip of the AST2500 FMC flash controller is strapped by 6228c2ecf20Sopenharmony_ci * hardware, or autodetected, but other chips need to be set. Enforce 6238c2ecf20Sopenharmony_ci * the 4B setting for all chips. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 6288c2ecf20Sopenharmony_ci u32 reg; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci reg = readl(controller->regs + CE_CONTROL_REG); 6318c2ecf20Sopenharmony_ci reg |= 1 << chip->cs; 6328c2ecf20Sopenharmony_ci writel(reg, controller->regs + CE_CONTROL_REG); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* 6368c2ecf20Sopenharmony_ci * The AST2400 SPI flash controller does not have a CE Control 6378c2ecf20Sopenharmony_ci * register. It uses the CE0 control register to set 4Byte mode at the 6388c2ecf20Sopenharmony_ci * controller level. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_cistatic void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci chip->ctl_val[smc_base] |= CONTROL_IO_ADDRESS_4B; 6438c2ecf20Sopenharmony_ci chip->ctl_val[smc_read] |= CONTROL_IO_ADDRESS_4B; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip, 6478c2ecf20Sopenharmony_ci struct resource *res) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 6508c2ecf20Sopenharmony_ci const struct aspeed_smc_info *info = controller->info; 6518c2ecf20Sopenharmony_ci u32 reg, base_reg; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* 6548c2ecf20Sopenharmony_ci * Always turn on the write enable bit to allow opcodes to be 6558c2ecf20Sopenharmony_ci * sent in user mode. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci aspeed_smc_chip_enable_write(chip); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* The driver only supports SPI type flash */ 6608c2ecf20Sopenharmony_ci if (info->hastype) 6618c2ecf20Sopenharmony_ci aspeed_smc_chip_set_type(chip, smc_type_spi); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* 6648c2ecf20Sopenharmony_ci * Configure chip base address in memory 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_ci chip->ahb_base = aspeed_smc_chip_base(chip, res); 6678c2ecf20Sopenharmony_ci if (!chip->ahb_base) { 6688c2ecf20Sopenharmony_ci dev_warn(chip->nor.dev, "CE%d window closed", chip->cs); 6698c2ecf20Sopenharmony_ci return -EINVAL; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* 6738c2ecf20Sopenharmony_ci * Get value of the inherited control register. U-Boot usually 6748c2ecf20Sopenharmony_ci * does some timing calibration on the FMC chip, so it's good 6758c2ecf20Sopenharmony_ci * to keep them. In the future, we should handle calibration 6768c2ecf20Sopenharmony_ci * from Linux. 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_ci reg = readl(chip->ctl); 6798c2ecf20Sopenharmony_ci dev_dbg(controller->dev, "control register: %08x\n", reg); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci base_reg = reg & CONTROL_KEEP_MASK; 6828c2ecf20Sopenharmony_ci if (base_reg != reg) { 6838c2ecf20Sopenharmony_ci dev_dbg(controller->dev, 6848c2ecf20Sopenharmony_ci "control register changed to: %08x\n", 6858c2ecf20Sopenharmony_ci base_reg); 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci chip->ctl_val[smc_base] = base_reg; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* 6908c2ecf20Sopenharmony_ci * Retain the prior value of the control register as the 6918c2ecf20Sopenharmony_ci * default if it was normal access mode. Otherwise start with 6928c2ecf20Sopenharmony_ci * the sanitized base value set to read mode. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci if ((reg & CONTROL_COMMAND_MODE_MASK) == 6958c2ecf20Sopenharmony_ci CONTROL_COMMAND_MODE_NORMAL) 6968c2ecf20Sopenharmony_ci chip->ctl_val[smc_read] = reg; 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci chip->ctl_val[smc_read] = chip->ctl_val[smc_base] | 6998c2ecf20Sopenharmony_ci CONTROL_COMMAND_MODE_NORMAL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci dev_dbg(controller->dev, "default control register: %08x\n", 7028c2ecf20Sopenharmony_ci chip->ctl_val[smc_read]); 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller = chip->controller; 7098c2ecf20Sopenharmony_ci const struct aspeed_smc_info *info = controller->info; 7108c2ecf20Sopenharmony_ci u32 cmd; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (chip->nor.addr_width == 4 && info->set_4b) 7138c2ecf20Sopenharmony_ci info->set_4b(chip); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* This is for direct AHB access when using Command Mode. */ 7168c2ecf20Sopenharmony_ci chip->ahb_window_size = aspeed_smc_chip_set_segment(chip); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * base mode has not been optimized yet. use it for writes. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_ci chip->ctl_val[smc_write] = chip->ctl_val[smc_base] | 7228c2ecf20Sopenharmony_ci chip->nor.program_opcode << CONTROL_COMMAND_SHIFT | 7238c2ecf20Sopenharmony_ci CONTROL_COMMAND_MODE_WRITE; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci dev_dbg(controller->dev, "write control register: %08x\n", 7268c2ecf20Sopenharmony_ci chip->ctl_val[smc_write]); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* 7298c2ecf20Sopenharmony_ci * TODO: Adjust clocks if fast read is supported and interpret 7308c2ecf20Sopenharmony_ci * SPI NOR flags to adjust controller settings. 7318c2ecf20Sopenharmony_ci */ 7328c2ecf20Sopenharmony_ci if (chip->nor.read_proto == SNOR_PROTO_1_1_1) { 7338c2ecf20Sopenharmony_ci if (chip->nor.read_dummy == 0) 7348c2ecf20Sopenharmony_ci cmd = CONTROL_COMMAND_MODE_NORMAL; 7358c2ecf20Sopenharmony_ci else 7368c2ecf20Sopenharmony_ci cmd = CONTROL_COMMAND_MODE_FREAD; 7378c2ecf20Sopenharmony_ci } else { 7388c2ecf20Sopenharmony_ci dev_err(chip->nor.dev, "unsupported SPI read mode\n"); 7398c2ecf20Sopenharmony_ci return -EINVAL; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci chip->ctl_val[smc_read] |= cmd | 7438c2ecf20Sopenharmony_ci CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci dev_dbg(controller->dev, "base control register: %08x\n", 7468c2ecf20Sopenharmony_ci chip->ctl_val[smc_read]); 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic const struct spi_nor_controller_ops aspeed_smc_controller_ops = { 7518c2ecf20Sopenharmony_ci .prepare = aspeed_smc_prep, 7528c2ecf20Sopenharmony_ci .unprepare = aspeed_smc_unprep, 7538c2ecf20Sopenharmony_ci .read_reg = aspeed_smc_read_reg, 7548c2ecf20Sopenharmony_ci .write_reg = aspeed_smc_write_reg, 7558c2ecf20Sopenharmony_ci .read = aspeed_smc_read_user, 7568c2ecf20Sopenharmony_ci .write = aspeed_smc_write_user, 7578c2ecf20Sopenharmony_ci}; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, 7608c2ecf20Sopenharmony_ci struct device_node *np, struct resource *r) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci const struct spi_nor_hwcaps hwcaps = { 7638c2ecf20Sopenharmony_ci .mask = SNOR_HWCAPS_READ | 7648c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_FAST | 7658c2ecf20Sopenharmony_ci SNOR_HWCAPS_PP, 7668c2ecf20Sopenharmony_ci }; 7678c2ecf20Sopenharmony_ci const struct aspeed_smc_info *info = controller->info; 7688c2ecf20Sopenharmony_ci struct device *dev = controller->dev; 7698c2ecf20Sopenharmony_ci struct device_node *child; 7708c2ecf20Sopenharmony_ci unsigned int cs; 7718c2ecf20Sopenharmony_ci int ret = -ENODEV; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, child) { 7748c2ecf20Sopenharmony_ci struct aspeed_smc_chip *chip; 7758c2ecf20Sopenharmony_ci struct spi_nor *nor; 7768c2ecf20Sopenharmony_ci struct mtd_info *mtd; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* This driver does not support NAND or NOR flash devices. */ 7798c2ecf20Sopenharmony_ci if (!of_device_is_compatible(child, "jedec,spi-nor")) 7808c2ecf20Sopenharmony_ci continue; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ret = of_property_read_u32(child, "reg", &cs); 7838c2ecf20Sopenharmony_ci if (ret) { 7848c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't not read chip select.\n"); 7858c2ecf20Sopenharmony_ci break; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (cs >= info->nce) { 7898c2ecf20Sopenharmony_ci dev_err(dev, "Chip select %d out of range.\n", 7908c2ecf20Sopenharmony_ci cs); 7918c2ecf20Sopenharmony_ci ret = -ERANGE; 7928c2ecf20Sopenharmony_ci break; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (controller->chips[cs]) { 7968c2ecf20Sopenharmony_ci dev_err(dev, "Chip select %d already in use by %s\n", 7978c2ecf20Sopenharmony_ci cs, dev_name(controller->chips[cs]->nor.dev)); 7988c2ecf20Sopenharmony_ci ret = -EBUSY; 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci chip = devm_kzalloc(controller->dev, sizeof(*chip), GFP_KERNEL); 8038c2ecf20Sopenharmony_ci if (!chip) { 8048c2ecf20Sopenharmony_ci ret = -ENOMEM; 8058c2ecf20Sopenharmony_ci break; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci chip->controller = controller; 8098c2ecf20Sopenharmony_ci chip->ctl = controller->regs + info->ctl0 + cs * 4; 8108c2ecf20Sopenharmony_ci chip->cs = cs; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci nor = &chip->nor; 8138c2ecf20Sopenharmony_ci mtd = &nor->mtd; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci nor->dev = dev; 8168c2ecf20Sopenharmony_ci nor->priv = chip; 8178c2ecf20Sopenharmony_ci spi_nor_set_flash_node(nor, child); 8188c2ecf20Sopenharmony_ci nor->controller_ops = &aspeed_smc_controller_ops; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ret = aspeed_smc_chip_setup_init(chip, r); 8218c2ecf20Sopenharmony_ci if (ret) 8228c2ecf20Sopenharmony_ci break; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci /* 8258c2ecf20Sopenharmony_ci * TODO: Add support for Dual and Quad SPI protocols 8268c2ecf20Sopenharmony_ci * attach when board support is present as determined 8278c2ecf20Sopenharmony_ci * by of property. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci ret = spi_nor_scan(nor, NULL, &hwcaps); 8308c2ecf20Sopenharmony_ci if (ret) 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci ret = aspeed_smc_chip_setup_finish(chip); 8348c2ecf20Sopenharmony_ci if (ret) 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 8388c2ecf20Sopenharmony_ci if (ret) 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci controller->chips[cs] = chip; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (ret) { 8458c2ecf20Sopenharmony_ci of_node_put(child); 8468c2ecf20Sopenharmony_ci aspeed_smc_unregister(controller); 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic int aspeed_smc_probe(struct platform_device *pdev) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 8558c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8568c2ecf20Sopenharmony_ci struct aspeed_smc_controller *controller; 8578c2ecf20Sopenharmony_ci const struct of_device_id *match; 8588c2ecf20Sopenharmony_ci const struct aspeed_smc_info *info; 8598c2ecf20Sopenharmony_ci struct resource *res; 8608c2ecf20Sopenharmony_ci int ret; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci match = of_match_device(aspeed_smc_matches, &pdev->dev); 8638c2ecf20Sopenharmony_ci if (!match || !match->data) 8648c2ecf20Sopenharmony_ci return -ENODEV; 8658c2ecf20Sopenharmony_ci info = match->data; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci controller = devm_kzalloc(&pdev->dev, 8688c2ecf20Sopenharmony_ci struct_size(controller, chips, info->nce), 8698c2ecf20Sopenharmony_ci GFP_KERNEL); 8708c2ecf20Sopenharmony_ci if (!controller) 8718c2ecf20Sopenharmony_ci return -ENOMEM; 8728c2ecf20Sopenharmony_ci controller->info = info; 8738c2ecf20Sopenharmony_ci controller->dev = dev; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci mutex_init(&controller->mutex); 8768c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, controller); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8798c2ecf20Sopenharmony_ci controller->regs = devm_ioremap_resource(dev, res); 8808c2ecf20Sopenharmony_ci if (IS_ERR(controller->regs)) 8818c2ecf20Sopenharmony_ci return PTR_ERR(controller->regs); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 8848c2ecf20Sopenharmony_ci controller->ahb_base = devm_ioremap_resource(dev, res); 8858c2ecf20Sopenharmony_ci if (IS_ERR(controller->ahb_base)) 8868c2ecf20Sopenharmony_ci return PTR_ERR(controller->ahb_base); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci controller->ahb_window_size = resource_size(res); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci ret = aspeed_smc_setup_flash(controller, np, res); 8918c2ecf20Sopenharmony_ci if (ret) 8928c2ecf20Sopenharmony_ci dev_err(dev, "Aspeed SMC probe failed %d\n", ret); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return ret; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic struct platform_driver aspeed_smc_driver = { 8988c2ecf20Sopenharmony_ci .probe = aspeed_smc_probe, 8998c2ecf20Sopenharmony_ci .remove = aspeed_smc_remove, 9008c2ecf20Sopenharmony_ci .driver = { 9018c2ecf20Sopenharmony_ci .name = DEVICE_NAME, 9028c2ecf20Sopenharmony_ci .of_match_table = aspeed_smc_matches, 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci}; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cimodule_platform_driver(aspeed_smc_driver); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASPEED Static Memory Controller Driver"); 9098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cedric Le Goater <clg@kaod.org>"); 9108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 911