162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) 662306a36Sopenharmony_ci * Copyright (c) 2003 Texas Instruments 762306a36Sopenharmony_ci * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> 1062306a36Sopenharmony_ci * Partially stolen from plat_nand.c 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Overview: 1362306a36Sopenharmony_ci * This is a device driver for the NAND flash device found on the 1462306a36Sopenharmony_ci * Amstrad E3 (Delta). 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2162306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2262306a36Sopenharmony_ci#include <linux/mtd/nand-gpio.h> 2362306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 2462306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/sizes.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * MTD structure for E3 (Delta) 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistruct gpio_nand { 3362306a36Sopenharmony_ci struct nand_controller base; 3462306a36Sopenharmony_ci struct nand_chip nand_chip; 3562306a36Sopenharmony_ci struct gpio_desc *gpiod_rdy; 3662306a36Sopenharmony_ci struct gpio_desc *gpiod_nce; 3762306a36Sopenharmony_ci struct gpio_desc *gpiod_nre; 3862306a36Sopenharmony_ci struct gpio_desc *gpiod_nwp; 3962306a36Sopenharmony_ci struct gpio_desc *gpiod_nwe; 4062306a36Sopenharmony_ci struct gpio_desc *gpiod_ale; 4162306a36Sopenharmony_ci struct gpio_desc *gpiod_cle; 4262306a36Sopenharmony_ci struct gpio_descs *data_gpiods; 4362306a36Sopenharmony_ci bool data_in; 4462306a36Sopenharmony_ci unsigned int tRP; 4562306a36Sopenharmony_ci unsigned int tWP; 4662306a36Sopenharmony_ci u8 (*io_read)(struct gpio_nand *this); 4762306a36Sopenharmony_ci void (*io_write)(struct gpio_nand *this, u8 byte); 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void gpio_nand_write_commit(struct gpio_nand *priv) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nwe, 1); 5362306a36Sopenharmony_ci ndelay(priv->tWP); 5462306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nwe, 0); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void gpio_nand_io_write(struct gpio_nand *priv, u8 byte) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct gpio_descs *data_gpiods = priv->data_gpiods; 6062306a36Sopenharmony_ci DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, 6362306a36Sopenharmony_ci data_gpiods->info, values); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci gpio_nand_write_commit(priv); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void gpio_nand_dir_output(struct gpio_nand *priv, u8 byte) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct gpio_descs *data_gpiods = priv->data_gpiods; 7162306a36Sopenharmony_ci DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; 7262306a36Sopenharmony_ci int i; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci for (i = 0; i < data_gpiods->ndescs; i++) 7562306a36Sopenharmony_ci gpiod_direction_output_raw(data_gpiods->desc[i], 7662306a36Sopenharmony_ci test_bit(i, values)); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci gpio_nand_write_commit(priv); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci priv->data_in = false; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic u8 gpio_nand_io_read(struct gpio_nand *priv) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci u8 res; 8662306a36Sopenharmony_ci struct gpio_descs *data_gpiods = priv->data_gpiods; 8762306a36Sopenharmony_ci DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nre, 1); 9062306a36Sopenharmony_ci ndelay(priv->tRP); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, 9362306a36Sopenharmony_ci data_gpiods->info, values); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nre, 0); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci res = values[0]; 9862306a36Sopenharmony_ci return res; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void gpio_nand_dir_input(struct gpio_nand *priv) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct gpio_descs *data_gpiods = priv->data_gpiods; 10462306a36Sopenharmony_ci int i; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (i = 0; i < data_gpiods->ndescs; i++) 10762306a36Sopenharmony_ci gpiod_direction_input(data_gpiods->desc[i]); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci priv->data_in = true; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void gpio_nand_write_buf(struct gpio_nand *priv, const u8 *buf, int len) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int i = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (len > 0 && priv->data_in) 11762306a36Sopenharmony_ci gpio_nand_dir_output(priv, buf[i++]); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci while (i < len) 12062306a36Sopenharmony_ci priv->io_write(priv, buf[i++]); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void gpio_nand_read_buf(struct gpio_nand *priv, u8 *buf, int len) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int i; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (priv->data_gpiods && !priv->data_in) 12862306a36Sopenharmony_ci gpio_nand_dir_input(priv); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci for (i = 0; i < len; i++) 13162306a36Sopenharmony_ci buf[i] = priv->io_read(priv); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void gpio_nand_ctrl_cs(struct gpio_nand *priv, bool assert) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nce, assert); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int gpio_nand_exec_op(struct nand_chip *this, 14062306a36Sopenharmony_ci const struct nand_operation *op, bool check_only) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct gpio_nand *priv = nand_get_controller_data(this); 14362306a36Sopenharmony_ci const struct nand_op_instr *instr; 14462306a36Sopenharmony_ci int ret = 0; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (check_only) 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci gpio_nand_ctrl_cs(priv, 1); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) { 15262306a36Sopenharmony_ci switch (instr->type) { 15362306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 15462306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_cle, 1); 15562306a36Sopenharmony_ci gpio_nand_write_buf(priv, &instr->ctx.cmd.opcode, 1); 15662306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_cle, 0); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 16062306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_ale, 1); 16162306a36Sopenharmony_ci gpio_nand_write_buf(priv, instr->ctx.addr.addrs, 16262306a36Sopenharmony_ci instr->ctx.addr.naddrs); 16362306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_ale, 0); 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 16762306a36Sopenharmony_ci gpio_nand_read_buf(priv, instr->ctx.data.buf.in, 16862306a36Sopenharmony_ci instr->ctx.data.len); 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 17262306a36Sopenharmony_ci gpio_nand_write_buf(priv, instr->ctx.data.buf.out, 17362306a36Sopenharmony_ci instr->ctx.data.len); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 17762306a36Sopenharmony_ci ret = priv->gpiod_rdy ? 17862306a36Sopenharmony_ci nand_gpio_waitrdy(this, priv->gpiod_rdy, 17962306a36Sopenharmony_ci instr->ctx.waitrdy.timeout_ms) : 18062306a36Sopenharmony_ci nand_soft_waitrdy(this, 18162306a36Sopenharmony_ci instr->ctx.waitrdy.timeout_ms); 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (ret) 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci gpio_nand_ctrl_cs(priv, 0); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int gpio_nand_setup_interface(struct nand_chip *this, int csline, 19562306a36Sopenharmony_ci const struct nand_interface_config *cf) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct gpio_nand *priv = nand_get_controller_data(this); 19862306a36Sopenharmony_ci const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf); 19962306a36Sopenharmony_ci struct device *dev = &nand_to_mtd(this)->dev; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (IS_ERR(sdr)) 20262306a36Sopenharmony_ci return PTR_ERR(sdr); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (priv->gpiod_nre) { 20862306a36Sopenharmony_ci priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); 20962306a36Sopenharmony_ci dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000); 21362306a36Sopenharmony_ci dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int gpio_nand_attach_chip(struct nand_chip *chip) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && 22162306a36Sopenharmony_ci chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 22262306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const struct nand_controller_ops gpio_nand_ops = { 22862306a36Sopenharmony_ci .exec_op = gpio_nand_exec_op, 22962306a36Sopenharmony_ci .attach_chip = gpio_nand_attach_chip, 23062306a36Sopenharmony_ci .setup_interface = gpio_nand_setup_interface, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* 23462306a36Sopenharmony_ci * Main initialization routine 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic int gpio_nand_probe(struct platform_device *pdev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); 23962306a36Sopenharmony_ci const struct mtd_partition *partitions = NULL; 24062306a36Sopenharmony_ci int num_partitions = 0; 24162306a36Sopenharmony_ci struct gpio_nand *priv; 24262306a36Sopenharmony_ci struct nand_chip *this; 24362306a36Sopenharmony_ci struct mtd_info *mtd; 24462306a36Sopenharmony_ci int (*probe)(struct platform_device *pdev, struct gpio_nand *priv); 24562306a36Sopenharmony_ci int err = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (pdata) { 24862306a36Sopenharmony_ci partitions = pdata->parts; 24962306a36Sopenharmony_ci num_partitions = pdata->num_parts; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Allocate memory for MTD device structure and private data */ 25362306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_nand), 25462306a36Sopenharmony_ci GFP_KERNEL); 25562306a36Sopenharmony_ci if (!priv) 25662306a36Sopenharmony_ci return -ENOMEM; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci this = &priv->nand_chip; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci mtd = nand_to_mtd(this); 26162306a36Sopenharmony_ci mtd->dev.parent = &pdev->dev; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci nand_set_controller_data(this, priv); 26462306a36Sopenharmony_ci nand_set_flash_node(this, pdev->dev.of_node); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN); 26762306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_rdy)) { 26862306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_rdy); 26962306a36Sopenharmony_ci dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err); 27062306a36Sopenharmony_ci return err; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Set chip enabled but write protected */ 27662306a36Sopenharmony_ci priv->gpiod_nwp = devm_gpiod_get_optional(&pdev->dev, "nwp", 27762306a36Sopenharmony_ci GPIOD_OUT_HIGH); 27862306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_nwp)) { 27962306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_nwp); 28062306a36Sopenharmony_ci dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); 28162306a36Sopenharmony_ci return err; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci priv->gpiod_nce = devm_gpiod_get_optional(&pdev->dev, "nce", 28562306a36Sopenharmony_ci GPIOD_OUT_LOW); 28662306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_nce)) { 28762306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_nce); 28862306a36Sopenharmony_ci dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); 28962306a36Sopenharmony_ci return err; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci priv->gpiod_nre = devm_gpiod_get_optional(&pdev->dev, "nre", 29362306a36Sopenharmony_ci GPIOD_OUT_LOW); 29462306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_nre)) { 29562306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_nre); 29662306a36Sopenharmony_ci dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci priv->gpiod_nwe = devm_gpiod_get_optional(&pdev->dev, "nwe", 30162306a36Sopenharmony_ci GPIOD_OUT_LOW); 30262306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_nwe)) { 30362306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_nwe); 30462306a36Sopenharmony_ci dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); 30562306a36Sopenharmony_ci return err; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW); 30962306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_ale)) { 31062306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_ale); 31162306a36Sopenharmony_ci dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err); 31262306a36Sopenharmony_ci return err; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW); 31662306a36Sopenharmony_ci if (IS_ERR(priv->gpiod_cle)) { 31762306a36Sopenharmony_ci err = PTR_ERR(priv->gpiod_cle); 31862306a36Sopenharmony_ci dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err); 31962306a36Sopenharmony_ci return err; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Request array of data pins, initialize them as input */ 32362306a36Sopenharmony_ci priv->data_gpiods = devm_gpiod_get_array_optional(&pdev->dev, "data", 32462306a36Sopenharmony_ci GPIOD_IN); 32562306a36Sopenharmony_ci if (IS_ERR(priv->data_gpiods)) { 32662306a36Sopenharmony_ci err = PTR_ERR(priv->data_gpiods); 32762306a36Sopenharmony_ci dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); 32862306a36Sopenharmony_ci return err; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci if (priv->data_gpiods) { 33162306a36Sopenharmony_ci if (!priv->gpiod_nwe) { 33262306a36Sopenharmony_ci dev_err(&pdev->dev, 33362306a36Sopenharmony_ci "mandatory NWE pin not provided by platform\n"); 33462306a36Sopenharmony_ci return -ENODEV; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci priv->io_read = gpio_nand_io_read; 33862306a36Sopenharmony_ci priv->io_write = gpio_nand_io_write; 33962306a36Sopenharmony_ci priv->data_in = true; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (pdev->id_entry) 34362306a36Sopenharmony_ci probe = (void *) pdev->id_entry->driver_data; 34462306a36Sopenharmony_ci else 34562306a36Sopenharmony_ci probe = of_device_get_match_data(&pdev->dev); 34662306a36Sopenharmony_ci if (probe) 34762306a36Sopenharmony_ci err = probe(pdev, priv); 34862306a36Sopenharmony_ci if (err) 34962306a36Sopenharmony_ci return err; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!priv->io_read || !priv->io_write) { 35262306a36Sopenharmony_ci dev_err(&pdev->dev, "incomplete device configuration\n"); 35362306a36Sopenharmony_ci return -ENODEV; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Initialize the NAND controller object embedded in gpio_nand. */ 35762306a36Sopenharmony_ci priv->base.ops = &gpio_nand_ops; 35862306a36Sopenharmony_ci nand_controller_init(&priv->base); 35962306a36Sopenharmony_ci this->controller = &priv->base; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * FIXME: We should release write protection only after nand_scan() to 36362306a36Sopenharmony_ci * be on the safe side but we can't do that until we have a generic way 36462306a36Sopenharmony_ci * to assert/deassert WP from the core. Even if the core shouldn't 36562306a36Sopenharmony_ci * write things in the nand_scan() path, it should have control on this 36662306a36Sopenharmony_ci * pin just in case we ever need to disable write protection during 36762306a36Sopenharmony_ci * chip detection/initialization. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci /* Release write protection */ 37062306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nwp, 0); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * This driver assumes that the default ECC engine should be TYPE_SOFT. 37462306a36Sopenharmony_ci * Set ->engine_type before registering the NAND devices in order to 37562306a36Sopenharmony_ci * provide a driver specific default value. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* Scan to find existence of the device */ 38062306a36Sopenharmony_ci err = nand_scan(this, 1); 38162306a36Sopenharmony_ci if (err) 38262306a36Sopenharmony_ci return err; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Register the partitions */ 38562306a36Sopenharmony_ci err = mtd_device_register(mtd, partitions, num_partitions); 38662306a36Sopenharmony_ci if (err) 38762306a36Sopenharmony_ci goto err_nand_cleanup; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cierr_nand_cleanup: 39262306a36Sopenharmony_ci nand_cleanup(this); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return err; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* 39862306a36Sopenharmony_ci * Clean up routine 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cistatic void gpio_nand_remove(struct platform_device *pdev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct gpio_nand *priv = platform_get_drvdata(pdev); 40362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); 40462306a36Sopenharmony_ci int ret; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Apply write protection */ 40762306a36Sopenharmony_ci gpiod_set_value(priv->gpiod_nwp, 1); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Unregister device */ 41062306a36Sopenharmony_ci ret = mtd_device_unregister(mtd); 41162306a36Sopenharmony_ci WARN_ON(ret); 41262306a36Sopenharmony_ci nand_cleanup(mtd_to_nand(mtd)); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci#ifdef CONFIG_OF 41662306a36Sopenharmony_cistatic const struct of_device_id gpio_nand_of_id_table[] = { 41762306a36Sopenharmony_ci { 41862306a36Sopenharmony_ci /* sentinel */ 41962306a36Sopenharmony_ci }, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_nand_of_id_table); 42262306a36Sopenharmony_ci#endif 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic const struct platform_device_id gpio_nand_plat_id_table[] = { 42562306a36Sopenharmony_ci { 42662306a36Sopenharmony_ci .name = "ams-delta-nand", 42762306a36Sopenharmony_ci }, { 42862306a36Sopenharmony_ci /* sentinel */ 42962306a36Sopenharmony_ci }, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct platform_driver gpio_nand_driver = { 43462306a36Sopenharmony_ci .probe = gpio_nand_probe, 43562306a36Sopenharmony_ci .remove_new = gpio_nand_remove, 43662306a36Sopenharmony_ci .id_table = gpio_nand_plat_id_table, 43762306a36Sopenharmony_ci .driver = { 43862306a36Sopenharmony_ci .name = "ams-delta-nand", 43962306a36Sopenharmony_ci .of_match_table = of_match_ptr(gpio_nand_of_id_table), 44062306a36Sopenharmony_ci }, 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cimodule_platform_driver(gpio_nand_driver); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 44662306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>"); 44762306a36Sopenharmony_ciMODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); 448