18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TXx9 NAND flash memory controller driver 48c2ecf20Sopenharmony_ci * Based on RBTX49xx patch from CELF patch archive. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (C) Copyright TOSHIBA CORPORATION 2004-2007 78c2ecf20Sopenharmony_ci * All Rights Reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h> 188c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/txx9/ndfmc.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* TXX9 NDFMC Registers */ 238c2ecf20Sopenharmony_ci#define TXX9_NDFDTR 0x00 248c2ecf20Sopenharmony_ci#define TXX9_NDFMCR 0x04 258c2ecf20Sopenharmony_ci#define TXX9_NDFSR 0x08 268c2ecf20Sopenharmony_ci#define TXX9_NDFISR 0x0c 278c2ecf20Sopenharmony_ci#define TXX9_NDFIMR 0x10 288c2ecf20Sopenharmony_ci#define TXX9_NDFSPR 0x14 298c2ecf20Sopenharmony_ci#define TXX9_NDFRSTR 0x18 /* not TX4939 */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* NDFMCR : NDFMC Mode Control */ 328c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_WE 0x80 338c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_ECC_ALL 0x60 348c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_ECC_RESET 0x60 358c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_ECC_READ 0x40 368c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_ECC_ON 0x20 378c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_ECC_OFF 0x00 388c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_CE 0x10 398c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ 408c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_ALE 0x02 418c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_CLE 0x01 428c2ecf20Sopenharmony_ci/* TX4939 only */ 438c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_X16 0x0400 448c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_MASK 0x0300 458c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 468c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_128 0x0100 478c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_256 0x0200 488c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_512 0x0300 498c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_CS_MASK 0x0c 508c2ecf20Sopenharmony_ci#define TXX9_NDFMCR_CS(ch) ((ch) << 2) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* NDFMCR : NDFMC Status */ 538c2ecf20Sopenharmony_ci#define TXX9_NDFSR_BUSY 0x80 548c2ecf20Sopenharmony_ci/* TX4939 only */ 558c2ecf20Sopenharmony_ci#define TXX9_NDFSR_DMARUN 0x40 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* NDFMCR : NDFMC Reset */ 588c2ecf20Sopenharmony_ci#define TXX9_NDFRSTR_RST 0x01 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct txx9ndfmc_priv { 618c2ecf20Sopenharmony_ci struct platform_device *dev; 628c2ecf20Sopenharmony_ci struct nand_chip chip; 638c2ecf20Sopenharmony_ci int cs; 648c2ecf20Sopenharmony_ci const char *mtdname; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define MAX_TXX9NDFMC_DEV 4 688c2ecf20Sopenharmony_cistruct txx9ndfmc_drvdata { 698c2ecf20Sopenharmony_ci struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; 708c2ecf20Sopenharmony_ci void __iomem *base; 718c2ecf20Sopenharmony_ci unsigned char hold; /* in gbusclock */ 728c2ecf20Sopenharmony_ci unsigned char spw; /* in gbusclock */ 738c2ecf20Sopenharmony_ci struct nand_controller controller; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct platform_device *mtd_to_platdev(struct mtd_info *mtd) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 798c2ecf20Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); 808c2ecf20Sopenharmony_ci return txx9_priv->dev; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); 868c2ecf20Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return drvdata->base + (reg << plat->shift); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci return __raw_readl(ndregaddr(dev, reg)); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void txx9ndfmc_write(struct platform_device *dev, 978c2ecf20Sopenharmony_ci u32 val, unsigned int reg) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci __raw_writel(val, ndregaddr(dev, reg)); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic uint8_t txx9ndfmc_read_byte(struct nand_chip *chip) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return txx9ndfmc_read(dev, TXX9_NDFDTR); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void txx9ndfmc_write_buf(struct nand_chip *chip, const uint8_t *buf, 1108c2ecf20Sopenharmony_ci int len) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 1138c2ecf20Sopenharmony_ci void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); 1148c2ecf20Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); 1178c2ecf20Sopenharmony_ci while (len--) 1188c2ecf20Sopenharmony_ci __raw_writel(*buf++, ndfdtr); 1198c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void txx9ndfmc_read_buf(struct nand_chip *chip, uint8_t *buf, int len) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 1258c2ecf20Sopenharmony_ci void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci while (len--) 1288c2ecf20Sopenharmony_ci *buf++ = __raw_readl(ndfdtr); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void txx9ndfmc_cmd_ctrl(struct nand_chip *chip, int cmd, 1328c2ecf20Sopenharmony_ci unsigned int ctrl) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); 1358c2ecf20Sopenharmony_ci struct platform_device *dev = txx9_priv->dev; 1368c2ecf20Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (ctrl & NAND_CTRL_CHANGE) { 1398c2ecf20Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); 1428c2ecf20Sopenharmony_ci mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; 1438c2ecf20Sopenharmony_ci mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; 1448c2ecf20Sopenharmony_ci /* TXX9_NDFMCR_CE bit is 0:high 1:low */ 1458c2ecf20Sopenharmony_ci mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; 1468c2ecf20Sopenharmony_ci if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { 1478c2ecf20Sopenharmony_ci mcr &= ~TXX9_NDFMCR_CS_MASK; 1488c2ecf20Sopenharmony_ci mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci if (cmd != NAND_CMD_NONE) 1538c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); 1548c2ecf20Sopenharmony_ci if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { 1558c2ecf20Sopenharmony_ci /* dummy write to update external latch */ 1568c2ecf20Sopenharmony_ci if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) 1578c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, 0, TXX9_NDFDTR); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int txx9ndfmc_dev_ready(struct nand_chip *chip) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int txx9ndfmc_calculate_ecc(struct nand_chip *chip, const uint8_t *dat, 1698c2ecf20Sopenharmony_ci uint8_t *ecc_code) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 1728c2ecf20Sopenharmony_ci int eccbytes; 1738c2ecf20Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mcr &= ~TXX9_NDFMCR_ECC_ALL; 1768c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); 1778c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); 1788c2ecf20Sopenharmony_ci for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { 1798c2ecf20Sopenharmony_ci ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); 1808c2ecf20Sopenharmony_ci ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); 1818c2ecf20Sopenharmony_ci ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); 1828c2ecf20Sopenharmony_ci ecc_code += 3; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf, 1898c2ecf20Sopenharmony_ci unsigned char *read_ecc, 1908c2ecf20Sopenharmony_ci unsigned char *calc_ecc) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int eccsize; 1938c2ecf20Sopenharmony_ci int corrected = 0; 1948c2ecf20Sopenharmony_ci int stat; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { 1978c2ecf20Sopenharmony_ci stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256, 1988c2ecf20Sopenharmony_ci false); 1998c2ecf20Sopenharmony_ci if (stat < 0) 2008c2ecf20Sopenharmony_ci return stat; 2018c2ecf20Sopenharmony_ci corrected += stat; 2028c2ecf20Sopenharmony_ci buf += 256; 2038c2ecf20Sopenharmony_ci read_ecc += 3; 2048c2ecf20Sopenharmony_ci calc_ecc += 3; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci return corrected; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void txx9ndfmc_enable_hwecc(struct nand_chip *chip, int mode) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 2128c2ecf20Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci mcr &= ~TXX9_NDFMCR_ECC_ALL; 2158c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); 2168c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); 2178c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void txx9ndfmc_initialize(struct platform_device *dev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 2238c2ecf20Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); 2248c2ecf20Sopenharmony_ci int tmout = 100; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) 2278c2ecf20Sopenharmony_ci ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ 2288c2ecf20Sopenharmony_ci else { 2298c2ecf20Sopenharmony_ci /* reset NDFMC */ 2308c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, 2318c2ecf20Sopenharmony_ci txx9ndfmc_read(dev, TXX9_NDFRSTR) | 2328c2ecf20Sopenharmony_ci TXX9_NDFRSTR_RST, 2338c2ecf20Sopenharmony_ci TXX9_NDFRSTR); 2348c2ecf20Sopenharmony_ci while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { 2358c2ecf20Sopenharmony_ci if (--tmout == 0) { 2368c2ecf20Sopenharmony_ci dev_err(&dev->dev, "reset failed.\n"); 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci udelay(1); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci /* setup Hold Time, Strobe Pulse Width */ 2438c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); 2448c2ecf20Sopenharmony_ci txx9ndfmc_write(dev, 2458c2ecf20Sopenharmony_ci (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? 2468c2ecf20Sopenharmony_ci TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ 2508c2ecf20Sopenharmony_ci DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int txx9ndfmc_attach_chip(struct nand_chip *chip) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci chip->ecc.strength = 1; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (mtd->writesize >= 512) { 2628c2ecf20Sopenharmony_ci chip->ecc.size = 512; 2638c2ecf20Sopenharmony_ci chip->ecc.bytes = 6; 2648c2ecf20Sopenharmony_ci } else { 2658c2ecf20Sopenharmony_ci chip->ecc.size = 256; 2668c2ecf20Sopenharmony_ci chip->ecc.bytes = 3; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci chip->ecc.calculate = txx9ndfmc_calculate_ecc; 2708c2ecf20Sopenharmony_ci chip->ecc.correct = txx9ndfmc_correct_data; 2718c2ecf20Sopenharmony_ci chip->ecc.hwctl = txx9ndfmc_enable_hwecc; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct nand_controller_ops txx9ndfmc_controller_ops = { 2778c2ecf20Sopenharmony_ci .attach_chip = txx9ndfmc_attach_chip, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int __init txx9ndfmc_probe(struct platform_device *dev) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 2838c2ecf20Sopenharmony_ci int hold, spw; 2848c2ecf20Sopenharmony_ci int i; 2858c2ecf20Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata; 2868c2ecf20Sopenharmony_ci unsigned long gbusclk = plat->gbus_clock; 2878c2ecf20Sopenharmony_ci struct resource *res; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); 2908c2ecf20Sopenharmony_ci if (!drvdata) 2918c2ecf20Sopenharmony_ci return -ENOMEM; 2928c2ecf20Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 0); 2938c2ecf20Sopenharmony_ci drvdata->base = devm_ioremap_resource(&dev->dev, res); 2948c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->base)) 2958c2ecf20Sopenharmony_ci return PTR_ERR(drvdata->base); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci hold = plat->hold ?: 20; /* tDH */ 2988c2ecf20Sopenharmony_ci spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); 3018c2ecf20Sopenharmony_ci spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); 3028c2ecf20Sopenharmony_ci if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) 3038c2ecf20Sopenharmony_ci hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ 3048c2ecf20Sopenharmony_ci spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ 3058c2ecf20Sopenharmony_ci hold = clamp(hold, 1, 15); 3068c2ecf20Sopenharmony_ci drvdata->hold = hold; 3078c2ecf20Sopenharmony_ci spw = clamp(spw, 1, 15); 3088c2ecf20Sopenharmony_ci drvdata->spw = spw; 3098c2ecf20Sopenharmony_ci dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", 3108c2ecf20Sopenharmony_ci (gbusclk + 500000) / 1000000, hold, spw); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci nand_controller_init(&drvdata->controller); 3138c2ecf20Sopenharmony_ci drvdata->controller.ops = &txx9ndfmc_controller_ops; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci platform_set_drvdata(dev, drvdata); 3168c2ecf20Sopenharmony_ci txx9ndfmc_initialize(dev); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { 3198c2ecf20Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv; 3208c2ecf20Sopenharmony_ci struct nand_chip *chip; 3218c2ecf20Sopenharmony_ci struct mtd_info *mtd; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!(plat->ch_mask & (1 << i))) 3248c2ecf20Sopenharmony_ci continue; 3258c2ecf20Sopenharmony_ci txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), 3268c2ecf20Sopenharmony_ci GFP_KERNEL); 3278c2ecf20Sopenharmony_ci if (!txx9_priv) 3288c2ecf20Sopenharmony_ci continue; 3298c2ecf20Sopenharmony_ci chip = &txx9_priv->chip; 3308c2ecf20Sopenharmony_ci mtd = nand_to_mtd(chip); 3318c2ecf20Sopenharmony_ci mtd->dev.parent = &dev->dev; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci chip->legacy.read_byte = txx9ndfmc_read_byte; 3348c2ecf20Sopenharmony_ci chip->legacy.read_buf = txx9ndfmc_read_buf; 3358c2ecf20Sopenharmony_ci chip->legacy.write_buf = txx9ndfmc_write_buf; 3368c2ecf20Sopenharmony_ci chip->legacy.cmd_ctrl = txx9ndfmc_cmd_ctrl; 3378c2ecf20Sopenharmony_ci chip->legacy.dev_ready = txx9ndfmc_dev_ready; 3388c2ecf20Sopenharmony_ci chip->legacy.chip_delay = 100; 3398c2ecf20Sopenharmony_ci chip->controller = &drvdata->controller; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci nand_set_controller_data(chip, txx9_priv); 3428c2ecf20Sopenharmony_ci txx9_priv->dev = dev; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (plat->ch_mask != 1) { 3458c2ecf20Sopenharmony_ci txx9_priv->cs = i; 3468c2ecf20Sopenharmony_ci txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", 3478c2ecf20Sopenharmony_ci dev_name(&dev->dev), i); 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci txx9_priv->cs = -1; 3508c2ecf20Sopenharmony_ci txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), 3518c2ecf20Sopenharmony_ci GFP_KERNEL); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci if (!txx9_priv->mtdname) { 3548c2ecf20Sopenharmony_ci kfree(txx9_priv); 3558c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Unable to allocate MTD name.\n"); 3568c2ecf20Sopenharmony_ci continue; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci if (plat->wide_mask & (1 << i)) 3598c2ecf20Sopenharmony_ci chip->options |= NAND_BUSWIDTH_16; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (nand_scan(chip, 1)) { 3628c2ecf20Sopenharmony_ci kfree(txx9_priv->mtdname); 3638c2ecf20Sopenharmony_ci kfree(txx9_priv); 3648c2ecf20Sopenharmony_ci continue; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci mtd->name = txx9_priv->mtdname; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci mtd_device_register(mtd, NULL, 0); 3698c2ecf20Sopenharmony_ci drvdata->mtds[i] = mtd; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int __exit txx9ndfmc_remove(struct platform_device *dev) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); 3788c2ecf20Sopenharmony_ci int ret, i; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!drvdata) 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { 3838c2ecf20Sopenharmony_ci struct mtd_info *mtd = drvdata->mtds[i]; 3848c2ecf20Sopenharmony_ci struct nand_chip *chip; 3858c2ecf20Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!mtd) 3888c2ecf20Sopenharmony_ci continue; 3898c2ecf20Sopenharmony_ci chip = mtd_to_nand(mtd); 3908c2ecf20Sopenharmony_ci txx9_priv = nand_get_controller_data(chip); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 3938c2ecf20Sopenharmony_ci WARN_ON(ret); 3948c2ecf20Sopenharmony_ci nand_cleanup(chip); 3958c2ecf20Sopenharmony_ci kfree(txx9_priv->mtdname); 3968c2ecf20Sopenharmony_ci kfree(txx9_priv); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4028c2ecf20Sopenharmony_cistatic int txx9ndfmc_resume(struct platform_device *dev) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci if (platform_get_drvdata(dev)) 4058c2ecf20Sopenharmony_ci txx9ndfmc_initialize(dev); 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci#else 4098c2ecf20Sopenharmony_ci#define txx9ndfmc_resume NULL 4108c2ecf20Sopenharmony_ci#endif 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic struct platform_driver txx9ndfmc_driver = { 4138c2ecf20Sopenharmony_ci .remove = __exit_p(txx9ndfmc_remove), 4148c2ecf20Sopenharmony_ci .resume = txx9ndfmc_resume, 4158c2ecf20Sopenharmony_ci .driver = { 4168c2ecf20Sopenharmony_ci .name = "txx9ndfmc", 4178c2ecf20Sopenharmony_ci }, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cimodule_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); 4248c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:txx9ndfmc"); 425