18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ARM PL353 SMC driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 - 2018 Xilinx, Inc 68c2ecf20Sopenharmony_ci * Author: Punnaiah Choudary Kalluri <punnaiah@xilinx.com> 78c2ecf20Sopenharmony_ci * Author: Naga Sureshkumar Relli <nagasure@xilinx.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/pl353-smc.h> 188c2ecf20Sopenharmony_ci#include <linux/amba/bus.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Register definitions */ 218c2ecf20Sopenharmony_ci#define PL353_SMC_MEMC_STATUS_OFFS 0 /* Controller status reg, RO */ 228c2ecf20Sopenharmony_ci#define PL353_SMC_CFG_CLR_OFFS 0xC /* Clear config reg, WO */ 238c2ecf20Sopenharmony_ci#define PL353_SMC_DIRECT_CMD_OFFS 0x10 /* Direct command reg, WO */ 248c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_OFFS 0x14 /* Set cycles register, WO */ 258c2ecf20Sopenharmony_ci#define PL353_SMC_SET_OPMODE_OFFS 0x18 /* Set opmode register, WO */ 268c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_STATUS_OFFS 0x400 /* ECC status register */ 278c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_MEMCFG_OFFS 0x404 /* ECC mem config reg */ 288c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_MEMCMD1_OFFS 0x408 /* ECC mem cmd1 reg */ 298c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_MEMCMD2_OFFS 0x40C /* ECC mem cmd2 reg */ 308c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_VALUE0_OFFS 0x418 /* ECC value 0 reg */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Controller status register specific constants */ 338c2ecf20Sopenharmony_ci#define PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT 6 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Clear configuration register specific constants */ 368c2ecf20Sopenharmony_ci#define PL353_SMC_CFG_CLR_INT_CLR_1 0x10 378c2ecf20Sopenharmony_ci#define PL353_SMC_CFG_CLR_ECC_INT_DIS_1 0x40 388c2ecf20Sopenharmony_ci#define PL353_SMC_CFG_CLR_INT_DIS_1 0x2 398c2ecf20Sopenharmony_ci#define PL353_SMC_CFG_CLR_DEFAULT_MASK (PL353_SMC_CFG_CLR_INT_CLR_1 | \ 408c2ecf20Sopenharmony_ci PL353_SMC_CFG_CLR_ECC_INT_DIS_1 | \ 418c2ecf20Sopenharmony_ci PL353_SMC_CFG_CLR_INT_DIS_1) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Set cycles register specific constants */ 448c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T0_MASK 0xF 458c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T0_SHIFT 0 468c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T1_MASK 0xF 478c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T1_SHIFT 4 488c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T2_MASK 0x7 498c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T2_SHIFT 8 508c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T3_MASK 0x7 518c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T3_SHIFT 11 528c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T4_MASK 0x7 538c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T4_SHIFT 14 548c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T5_MASK 0x7 558c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T5_SHIFT 17 568c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T6_MASK 0xF 578c2ecf20Sopenharmony_ci#define PL353_SMC_SET_CYCLES_T6_SHIFT 20 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* ECC status register specific constants */ 608c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_STATUS_BUSY BIT(6) 618c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_REG_SIZE_OFFS 4 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* ECC memory config register specific constants */ 648c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_MEMCFG_MODE_MASK 0xC 658c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_MEMCFG_MODE_SHIFT 2 668c2ecf20Sopenharmony_ci#define PL353_SMC_ECC_MEMCFG_PGSIZE_MASK 0x3 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define PL353_SMC_DC_UPT_NAND_REGS ((4 << 23) | /* CS: NAND chip */ \ 698c2ecf20Sopenharmony_ci (2 << 21)) /* UpdateRegs operation */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define PL353_NAND_ECC_CMD1 ((0x80) | /* Write command */ \ 728c2ecf20Sopenharmony_ci (0 << 8) | /* Read command */ \ 738c2ecf20Sopenharmony_ci (0x30 << 16) | /* Read End command */ \ 748c2ecf20Sopenharmony_ci (1 << 24)) /* Read End command calid */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define PL353_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \ 778c2ecf20Sopenharmony_ci (5 << 8) | /* Read col change cmd */ \ 788c2ecf20Sopenharmony_ci (0xE0 << 16) | /* Read col change end cmd */ \ 798c2ecf20Sopenharmony_ci (1 << 24)) /* Read col change end cmd valid */ 808c2ecf20Sopenharmony_ci#define PL353_NAND_ECC_BUSY_TIMEOUT (1 * HZ) 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * struct pl353_smc_data - Private smc driver structure 838c2ecf20Sopenharmony_ci * @memclk: Pointer to the peripheral clock 848c2ecf20Sopenharmony_ci * @aclk: Pointer to the APER clock 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistruct pl353_smc_data { 878c2ecf20Sopenharmony_ci struct clk *memclk; 888c2ecf20Sopenharmony_ci struct clk *aclk; 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* SMC virtual register base */ 928c2ecf20Sopenharmony_cistatic void __iomem *pl353_smc_base; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/** 958c2ecf20Sopenharmony_ci * pl353_smc_set_buswidth - Set memory buswidth 968c2ecf20Sopenharmony_ci * @bw: Memory buswidth (8 | 16) 978c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ciint pl353_smc_set_buswidth(unsigned int bw) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci if (bw != PL353_SMC_MEM_WIDTH_8 && bw != PL353_SMC_MEM_WIDTH_16) 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci writel(bw, pl353_smc_base + PL353_SMC_SET_OPMODE_OFFS); 1058c2ecf20Sopenharmony_ci writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base + 1068c2ecf20Sopenharmony_ci PL353_SMC_DIRECT_CMD_OFFS); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_set_buswidth); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * pl353_smc_set_cycles - Set memory timing parameters 1148c2ecf20Sopenharmony_ci * @timings: NAND controller timing parameters 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Sets NAND chip specific timing parameters. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_civoid pl353_smc_set_cycles(u32 timings[]) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * Set write pulse timing. This one is easy to extract: 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * NWE_PULSE = tWP 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci timings[0] &= PL353_SMC_SET_CYCLES_T0_MASK; 1268c2ecf20Sopenharmony_ci timings[1] = (timings[1] & PL353_SMC_SET_CYCLES_T1_MASK) << 1278c2ecf20Sopenharmony_ci PL353_SMC_SET_CYCLES_T1_SHIFT; 1288c2ecf20Sopenharmony_ci timings[2] = (timings[2] & PL353_SMC_SET_CYCLES_T2_MASK) << 1298c2ecf20Sopenharmony_ci PL353_SMC_SET_CYCLES_T2_SHIFT; 1308c2ecf20Sopenharmony_ci timings[3] = (timings[3] & PL353_SMC_SET_CYCLES_T3_MASK) << 1318c2ecf20Sopenharmony_ci PL353_SMC_SET_CYCLES_T3_SHIFT; 1328c2ecf20Sopenharmony_ci timings[4] = (timings[4] & PL353_SMC_SET_CYCLES_T4_MASK) << 1338c2ecf20Sopenharmony_ci PL353_SMC_SET_CYCLES_T4_SHIFT; 1348c2ecf20Sopenharmony_ci timings[5] = (timings[5] & PL353_SMC_SET_CYCLES_T5_MASK) << 1358c2ecf20Sopenharmony_ci PL353_SMC_SET_CYCLES_T5_SHIFT; 1368c2ecf20Sopenharmony_ci timings[6] = (timings[6] & PL353_SMC_SET_CYCLES_T6_MASK) << 1378c2ecf20Sopenharmony_ci PL353_SMC_SET_CYCLES_T6_SHIFT; 1388c2ecf20Sopenharmony_ci timings[0] |= timings[1] | timings[2] | timings[3] | 1398c2ecf20Sopenharmony_ci timings[4] | timings[5] | timings[6]; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci writel(timings[0], pl353_smc_base + PL353_SMC_SET_CYCLES_OFFS); 1428c2ecf20Sopenharmony_ci writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base + 1438c2ecf20Sopenharmony_ci PL353_SMC_DIRECT_CMD_OFFS); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_set_cycles); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/** 1488c2ecf20Sopenharmony_ci * pl353_smc_ecc_is_busy - Read ecc busy flag 1498c2ecf20Sopenharmony_ci * Return: the ecc_status bit from the ecc_status register. 1 = busy, 0 = idle 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cibool pl353_smc_ecc_is_busy(void) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return ((readl(pl353_smc_base + PL353_SMC_ECC_STATUS_OFFS) & 1548c2ecf20Sopenharmony_ci PL353_SMC_ECC_STATUS_BUSY) == PL353_SMC_ECC_STATUS_BUSY); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_ecc_is_busy); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * pl353_smc_get_ecc_val - Read ecc_valueN registers 1608c2ecf20Sopenharmony_ci * @ecc_reg: Index of the ecc_value reg (0..3) 1618c2ecf20Sopenharmony_ci * Return: the content of the requested ecc_value register. 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * There are four valid ecc_value registers. The argument is truncated to stay 1648c2ecf20Sopenharmony_ci * within this valid boundary. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ciu32 pl353_smc_get_ecc_val(int ecc_reg) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci u32 addr, reg; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci addr = PL353_SMC_ECC_VALUE0_OFFS + 1718c2ecf20Sopenharmony_ci (ecc_reg * PL353_SMC_ECC_REG_SIZE_OFFS); 1728c2ecf20Sopenharmony_ci reg = readl(pl353_smc_base + addr); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return reg; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_get_ecc_val); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * pl353_smc_get_nand_int_status_raw - Get NAND interrupt status bit 1808c2ecf20Sopenharmony_ci * Return: the raw_int_status1 bit from the memc_status register 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ciint pl353_smc_get_nand_int_status_raw(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u32 reg; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci reg = readl(pl353_smc_base + PL353_SMC_MEMC_STATUS_OFFS); 1878c2ecf20Sopenharmony_ci reg >>= PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT; 1888c2ecf20Sopenharmony_ci reg &= 1; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return reg; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_get_nand_int_status_raw); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/** 1958c2ecf20Sopenharmony_ci * pl353_smc_clr_nand_int - Clear NAND interrupt 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_civoid pl353_smc_clr_nand_int(void) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci writel(PL353_SMC_CFG_CLR_INT_CLR_1, 2008c2ecf20Sopenharmony_ci pl353_smc_base + PL353_SMC_CFG_CLR_OFFS); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_clr_nand_int); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/** 2058c2ecf20Sopenharmony_ci * pl353_smc_set_ecc_mode - Set SMC ECC mode 2068c2ecf20Sopenharmony_ci * @mode: ECC mode (BYPASS, APB, MEM) 2078c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ciint pl353_smc_set_ecc_mode(enum pl353_smc_ecc_mode mode) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci u32 reg; 2128c2ecf20Sopenharmony_ci int ret = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (mode) { 2158c2ecf20Sopenharmony_ci case PL353_SMC_ECCMODE_BYPASS: 2168c2ecf20Sopenharmony_ci case PL353_SMC_ECCMODE_APB: 2178c2ecf20Sopenharmony_ci case PL353_SMC_ECCMODE_MEM: 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); 2208c2ecf20Sopenharmony_ci reg &= ~PL353_SMC_ECC_MEMCFG_MODE_MASK; 2218c2ecf20Sopenharmony_ci reg |= mode << PL353_SMC_ECC_MEMCFG_MODE_SHIFT; 2228c2ecf20Sopenharmony_ci writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci default: 2268c2ecf20Sopenharmony_ci ret = -EINVAL; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_set_ecc_mode); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * pl353_smc_set_ecc_pg_size - Set SMC ECC page size 2358c2ecf20Sopenharmony_ci * @pg_sz: ECC page size 2368c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ciint pl353_smc_set_ecc_pg_size(unsigned int pg_sz) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u32 reg, sz; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci switch (pg_sz) { 2438c2ecf20Sopenharmony_ci case 0: 2448c2ecf20Sopenharmony_ci sz = 0; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case SZ_512: 2478c2ecf20Sopenharmony_ci sz = 1; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case SZ_1K: 2508c2ecf20Sopenharmony_ci sz = 2; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case SZ_2K: 2538c2ecf20Sopenharmony_ci sz = 3; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci default: 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); 2608c2ecf20Sopenharmony_ci reg &= ~PL353_SMC_ECC_MEMCFG_PGSIZE_MASK; 2618c2ecf20Sopenharmony_ci reg |= sz; 2628c2ecf20Sopenharmony_ci writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pl353_smc_set_ecc_pg_size); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int __maybe_unused pl353_smc_suspend(struct device *dev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci clk_disable(pl353_smc->memclk); 2738c2ecf20Sopenharmony_ci clk_disable(pl353_smc->aclk); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int __maybe_unused pl353_smc_resume(struct device *dev) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ret = clk_enable(pl353_smc->aclk); 2848c2ecf20Sopenharmony_ci if (ret) { 2858c2ecf20Sopenharmony_ci dev_err(dev, "Cannot enable axi domain clock.\n"); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ret = clk_enable(pl353_smc->memclk); 2908c2ecf20Sopenharmony_ci if (ret) { 2918c2ecf20Sopenharmony_ci dev_err(dev, "Cannot enable memory clock.\n"); 2928c2ecf20Sopenharmony_ci clk_disable(pl353_smc->aclk); 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic struct amba_driver pl353_smc_driver; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend, 3028c2ecf20Sopenharmony_ci pl353_smc_resume); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * pl353_smc_init_nand_interface - Initialize the NAND interface 3068c2ecf20Sopenharmony_ci * @adev: Pointer to the amba_device struct 3078c2ecf20Sopenharmony_ci * @nand_node: Pointer to the pl353_nand device_node struct 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_cistatic void pl353_smc_init_nand_interface(struct amba_device *adev, 3108c2ecf20Sopenharmony_ci struct device_node *nand_node) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned long timeout; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci pl353_smc_set_buswidth(PL353_SMC_MEM_WIDTH_8); 3158c2ecf20Sopenharmony_ci writel(PL353_SMC_CFG_CLR_INT_CLR_1, 3168c2ecf20Sopenharmony_ci pl353_smc_base + PL353_SMC_CFG_CLR_OFFS); 3178c2ecf20Sopenharmony_ci writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base + 3188c2ecf20Sopenharmony_ci PL353_SMC_DIRECT_CMD_OFFS); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT; 3218c2ecf20Sopenharmony_ci /* Wait till the ECC operation is complete */ 3228c2ecf20Sopenharmony_ci do { 3238c2ecf20Sopenharmony_ci if (pl353_smc_ecc_is_busy()) 3248c2ecf20Sopenharmony_ci cpu_relax(); 3258c2ecf20Sopenharmony_ci else 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci } while (!time_after_eq(jiffies, timeout)); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) 3308c2ecf20Sopenharmony_ci return; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci writel(PL353_NAND_ECC_CMD1, 3338c2ecf20Sopenharmony_ci pl353_smc_base + PL353_SMC_ECC_MEMCMD1_OFFS); 3348c2ecf20Sopenharmony_ci writel(PL353_NAND_ECC_CMD2, 3358c2ecf20Sopenharmony_ci pl353_smc_base + PL353_SMC_ECC_MEMCMD2_OFFS); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic const struct of_device_id pl353_smc_supported_children[] = { 3398c2ecf20Sopenharmony_ci { 3408c2ecf20Sopenharmony_ci .compatible = "cfi-flash" 3418c2ecf20Sopenharmony_ci }, 3428c2ecf20Sopenharmony_ci { 3438c2ecf20Sopenharmony_ci .compatible = "arm,pl353-nand-r2p1", 3448c2ecf20Sopenharmony_ci .data = pl353_smc_init_nand_interface 3458c2ecf20Sopenharmony_ci }, 3468c2ecf20Sopenharmony_ci {} 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct pl353_smc_data *pl353_smc; 3528c2ecf20Sopenharmony_ci struct device_node *child; 3538c2ecf20Sopenharmony_ci struct resource *res; 3548c2ecf20Sopenharmony_ci int err; 3558c2ecf20Sopenharmony_ci struct device_node *of_node = adev->dev.of_node; 3568c2ecf20Sopenharmony_ci static void (*init)(struct amba_device *adev, 3578c2ecf20Sopenharmony_ci struct device_node *nand_node); 3588c2ecf20Sopenharmony_ci const struct of_device_id *match = NULL; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL); 3618c2ecf20Sopenharmony_ci if (!pl353_smc) 3628c2ecf20Sopenharmony_ci return -ENOMEM; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Get the NAND controller virtual address */ 3658c2ecf20Sopenharmony_ci res = &adev->res; 3668c2ecf20Sopenharmony_ci pl353_smc_base = devm_ioremap_resource(&adev->dev, res); 3678c2ecf20Sopenharmony_ci if (IS_ERR(pl353_smc_base)) 3688c2ecf20Sopenharmony_ci return PTR_ERR(pl353_smc_base); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk"); 3718c2ecf20Sopenharmony_ci if (IS_ERR(pl353_smc->aclk)) { 3728c2ecf20Sopenharmony_ci dev_err(&adev->dev, "aclk clock not found.\n"); 3738c2ecf20Sopenharmony_ci return PTR_ERR(pl353_smc->aclk); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk"); 3778c2ecf20Sopenharmony_ci if (IS_ERR(pl353_smc->memclk)) { 3788c2ecf20Sopenharmony_ci dev_err(&adev->dev, "memclk clock not found.\n"); 3798c2ecf20Sopenharmony_ci return PTR_ERR(pl353_smc->memclk); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci err = clk_prepare_enable(pl353_smc->aclk); 3838c2ecf20Sopenharmony_ci if (err) { 3848c2ecf20Sopenharmony_ci dev_err(&adev->dev, "Unable to enable AXI clock.\n"); 3858c2ecf20Sopenharmony_ci return err; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = clk_prepare_enable(pl353_smc->memclk); 3898c2ecf20Sopenharmony_ci if (err) { 3908c2ecf20Sopenharmony_ci dev_err(&adev->dev, "Unable to enable memory clock.\n"); 3918c2ecf20Sopenharmony_ci goto out_clk_dis_aper; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci amba_set_drvdata(adev, pl353_smc); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* clear interrupts */ 3978c2ecf20Sopenharmony_ci writel(PL353_SMC_CFG_CLR_DEFAULT_MASK, 3988c2ecf20Sopenharmony_ci pl353_smc_base + PL353_SMC_CFG_CLR_OFFS); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Find compatible children. Only a single child is supported */ 4018c2ecf20Sopenharmony_ci for_each_available_child_of_node(of_node, child) { 4028c2ecf20Sopenharmony_ci match = of_match_node(pl353_smc_supported_children, child); 4038c2ecf20Sopenharmony_ci if (!match) { 4048c2ecf20Sopenharmony_ci dev_warn(&adev->dev, "unsupported child node\n"); 4058c2ecf20Sopenharmony_ci continue; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci if (!match) { 4108c2ecf20Sopenharmony_ci err = -ENODEV; 4118c2ecf20Sopenharmony_ci dev_err(&adev->dev, "no matching children\n"); 4128c2ecf20Sopenharmony_ci goto out_clk_disable; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci init = match->data; 4168c2ecf20Sopenharmony_ci if (init) 4178c2ecf20Sopenharmony_ci init(adev, child); 4188c2ecf20Sopenharmony_ci of_platform_device_create(child, NULL, &adev->dev); 4198c2ecf20Sopenharmony_ci of_node_put(child); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ciout_clk_disable: 4248c2ecf20Sopenharmony_ci clk_disable_unprepare(pl353_smc->memclk); 4258c2ecf20Sopenharmony_ciout_clk_dis_aper: 4268c2ecf20Sopenharmony_ci clk_disable_unprepare(pl353_smc->aclk); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return err; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic void pl353_smc_remove(struct amba_device *adev) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci clk_disable_unprepare(pl353_smc->memclk); 4368c2ecf20Sopenharmony_ci clk_disable_unprepare(pl353_smc->aclk); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic const struct amba_id pl353_ids[] = { 4408c2ecf20Sopenharmony_ci { 4418c2ecf20Sopenharmony_ci .id = 0x00041353, 4428c2ecf20Sopenharmony_ci .mask = 0x000fffff, 4438c2ecf20Sopenharmony_ci }, 4448c2ecf20Sopenharmony_ci { 0, 0 }, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl353_ids); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic struct amba_driver pl353_smc_driver = { 4498c2ecf20Sopenharmony_ci .drv = { 4508c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4518c2ecf20Sopenharmony_ci .name = "pl353-smc", 4528c2ecf20Sopenharmony_ci .pm = &pl353_smc_dev_pm_ops, 4538c2ecf20Sopenharmony_ci }, 4548c2ecf20Sopenharmony_ci .id_table = pl353_ids, 4558c2ecf20Sopenharmony_ci .probe = pl353_smc_probe, 4568c2ecf20Sopenharmony_ci .remove = pl353_smc_remove, 4578c2ecf20Sopenharmony_ci}; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cimodule_amba_driver(pl353_smc_driver); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx, Inc."); 4628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARM PL353 SMC Driver"); 4638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 464