162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2004 Richard Purdie 462306a36Sopenharmony_ci * Copyright (C) 2008 Dmitry Baryshkov 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on Sharp's NAND driver sharp_sl.c 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1362306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 1462306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1562306a36Sopenharmony_ci#include <linux/mtd/sharpsl.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct sharpsl_nand { 2162306a36Sopenharmony_ci struct nand_controller controller; 2262306a36Sopenharmony_ci struct nand_chip chip; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci void __iomem *io; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* register offset */ 3362306a36Sopenharmony_ci#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ 3462306a36Sopenharmony_ci#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ 3562306a36Sopenharmony_ci#define ECCCP 0x08 /* column parity 5 - 0 bit */ 3662306a36Sopenharmony_ci#define ECCCNTR 0x0C /* ECC byte counter */ 3762306a36Sopenharmony_ci#define ECCCLRR 0x10 /* cleare ECC */ 3862306a36Sopenharmony_ci#define FLASHIO 0x14 /* Flash I/O */ 3962306a36Sopenharmony_ci#define FLASHCTL 0x18 /* Flash Control */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Flash control bit */ 4262306a36Sopenharmony_ci#define FLRYBY (1 << 5) 4362306a36Sopenharmony_ci#define FLCE1 (1 << 4) 4462306a36Sopenharmony_ci#define FLWP (1 << 3) 4562306a36Sopenharmony_ci#define FLALE (1 << 2) 4662306a36Sopenharmony_ci#define FLCLE (1 << 1) 4762306a36Sopenharmony_ci#define FLCE0 (1 << 0) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * hardware specific access to control-lines 5162306a36Sopenharmony_ci * ctrl: 5262306a36Sopenharmony_ci * NAND_CNE: bit 0 -> ! bit 0 & 4 5362306a36Sopenharmony_ci * NAND_CLE: bit 1 -> bit 1 5462306a36Sopenharmony_ci * NAND_ALE: bit 2 -> bit 2 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic void sharpsl_nand_hwcontrol(struct nand_chip *chip, int cmd, 5862306a36Sopenharmony_ci unsigned int ctrl) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (ctrl & NAND_CTRL_CHANGE) { 6362306a36Sopenharmony_ci unsigned char bits = ctrl & 0x07; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci bits |= (ctrl & 0x01) << 4; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci bits ^= 0x11; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (cmd != NAND_CMD_NONE) 7362306a36Sopenharmony_ci writeb(cmd, chip->legacy.IO_ADDR_W); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int sharpsl_nand_dev_ready(struct nand_chip *chip) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 7962306a36Sopenharmony_ci return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void sharpsl_nand_enable_hwecc(struct nand_chip *chip, int mode) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 8562306a36Sopenharmony_ci writeb(0, sharpsl->io + ECCCLRR); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int sharpsl_nand_calculate_ecc(struct nand_chip *chip, 8962306a36Sopenharmony_ci const u_char * dat, u_char * ecc_code) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 9262306a36Sopenharmony_ci ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); 9362306a36Sopenharmony_ci ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); 9462306a36Sopenharmony_ci ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; 9562306a36Sopenharmony_ci return readb(sharpsl->io + ECCCNTR) != 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int sharpsl_attach_chip(struct nand_chip *chip) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci chip->ecc.size = 256; 10462306a36Sopenharmony_ci chip->ecc.bytes = 3; 10562306a36Sopenharmony_ci chip->ecc.strength = 1; 10662306a36Sopenharmony_ci chip->ecc.hwctl = sharpsl_nand_enable_hwecc; 10762306a36Sopenharmony_ci chip->ecc.calculate = sharpsl_nand_calculate_ecc; 10862306a36Sopenharmony_ci chip->ecc.correct = rawnand_sw_hamming_correct; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct nand_controller_ops sharpsl_ops = { 11462306a36Sopenharmony_ci .attach_chip = sharpsl_attach_chip, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * Main initialization routine 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistatic int sharpsl_nand_probe(struct platform_device *pdev) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct nand_chip *this; 12362306a36Sopenharmony_ci struct mtd_info *mtd; 12462306a36Sopenharmony_ci struct resource *r; 12562306a36Sopenharmony_ci int err = 0; 12662306a36Sopenharmony_ci struct sharpsl_nand *sharpsl; 12762306a36Sopenharmony_ci struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!data) { 13062306a36Sopenharmony_ci dev_err(&pdev->dev, "no platform data!\n"); 13162306a36Sopenharmony_ci return -EINVAL; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Allocate memory for MTD device structure and private data */ 13562306a36Sopenharmony_ci sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); 13662306a36Sopenharmony_ci if (!sharpsl) 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14062306a36Sopenharmony_ci if (!r) { 14162306a36Sopenharmony_ci dev_err(&pdev->dev, "no io memory resource defined!\n"); 14262306a36Sopenharmony_ci err = -ENODEV; 14362306a36Sopenharmony_ci goto err_get_res; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* map physical address */ 14762306a36Sopenharmony_ci sharpsl->io = ioremap(r->start, resource_size(r)); 14862306a36Sopenharmony_ci if (!sharpsl->io) { 14962306a36Sopenharmony_ci dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n"); 15062306a36Sopenharmony_ci err = -EIO; 15162306a36Sopenharmony_ci goto err_ioremap; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Get pointer to private data */ 15562306a36Sopenharmony_ci this = (struct nand_chip *)(&sharpsl->chip); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci nand_controller_init(&sharpsl->controller); 15862306a36Sopenharmony_ci sharpsl->controller.ops = &sharpsl_ops; 15962306a36Sopenharmony_ci this->controller = &sharpsl->controller; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Link the private data with the MTD structure */ 16262306a36Sopenharmony_ci mtd = nand_to_mtd(this); 16362306a36Sopenharmony_ci mtd->dev.parent = &pdev->dev; 16462306a36Sopenharmony_ci mtd_set_ooblayout(mtd, data->ecc_layout); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci platform_set_drvdata(pdev, sharpsl); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * PXA initialize 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Set address of NAND IO lines */ 17462306a36Sopenharmony_ci this->legacy.IO_ADDR_R = sharpsl->io + FLASHIO; 17562306a36Sopenharmony_ci this->legacy.IO_ADDR_W = sharpsl->io + FLASHIO; 17662306a36Sopenharmony_ci /* Set address of hardware control function */ 17762306a36Sopenharmony_ci this->legacy.cmd_ctrl = sharpsl_nand_hwcontrol; 17862306a36Sopenharmony_ci this->legacy.dev_ready = sharpsl_nand_dev_ready; 17962306a36Sopenharmony_ci /* 15 us command delay time */ 18062306a36Sopenharmony_ci this->legacy.chip_delay = 15; 18162306a36Sopenharmony_ci this->badblock_pattern = data->badblock_pattern; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Scan to find existence of the device */ 18462306a36Sopenharmony_ci err = nand_scan(this, 1); 18562306a36Sopenharmony_ci if (err) 18662306a36Sopenharmony_ci goto err_scan; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Register the partitions */ 18962306a36Sopenharmony_ci mtd->name = "sharpsl-nand"; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci err = mtd_device_parse_register(mtd, data->part_parsers, NULL, 19262306a36Sopenharmony_ci data->partitions, data->nr_partitions); 19362306a36Sopenharmony_ci if (err) 19462306a36Sopenharmony_ci goto err_add; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Return happy */ 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cierr_add: 20062306a36Sopenharmony_ci nand_cleanup(this); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cierr_scan: 20362306a36Sopenharmony_ci iounmap(sharpsl->io); 20462306a36Sopenharmony_cierr_ioremap: 20562306a36Sopenharmony_cierr_get_res: 20662306a36Sopenharmony_ci kfree(sharpsl); 20762306a36Sopenharmony_ci return err; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * Clean up routine 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic void sharpsl_nand_remove(struct platform_device *pdev) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); 21662306a36Sopenharmony_ci struct nand_chip *chip = &sharpsl->chip; 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Unregister device */ 22062306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 22162306a36Sopenharmony_ci WARN_ON(ret); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Release resources */ 22462306a36Sopenharmony_ci nand_cleanup(chip); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci iounmap(sharpsl->io); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Free the driver's structure */ 22962306a36Sopenharmony_ci kfree(sharpsl); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic struct platform_driver sharpsl_nand_driver = { 23362306a36Sopenharmony_ci .driver = { 23462306a36Sopenharmony_ci .name = "sharpsl-nand", 23562306a36Sopenharmony_ci }, 23662306a36Sopenharmony_ci .probe = sharpsl_nand_probe, 23762306a36Sopenharmony_ci .remove_new = sharpsl_nand_remove, 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cimodule_platform_driver(sharpsl_nand_driver); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 24362306a36Sopenharmony_ciMODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); 24462306a36Sopenharmony_ciMODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); 245