14857393dSBoris Brezillon // SPDX-License-Identifier: GPL-2.0 23d12c0c7SJonathan McDowell /* 33d12c0c7SJonathan McDowell * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> 43d12c0c7SJonathan McDowell * 5187c5448SBoris Brezillon * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) 67b6afee7SBoris Brezillon * Copyright (c) 2003 Texas Instruments 77b6afee7SBoris Brezillon * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> 87b6afee7SBoris Brezillon * 97e95d1f1SJanusz Krzysztofik * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> 1093cbd6f3SBoris Brezillon * Partially stolen from plat_nand.c 113d12c0c7SJonathan McDowell * 123d12c0c7SJonathan McDowell * Overview: 133d12c0c7SJonathan McDowell * This is a device driver for the NAND flash device found on the 143d12c0c7SJonathan McDowell * Amstrad E3 (Delta). 153d12c0c7SJonathan McDowell */ 163d12c0c7SJonathan McDowell 173d12c0c7SJonathan McDowell #include <linux/slab.h> 183d12c0c7SJonathan McDowell #include <linux/module.h> 193d12c0c7SJonathan McDowell #include <linux/delay.h> 20f1a97e0bSJanusz Krzysztofik #include <linux/gpio/consumer.h> 213d12c0c7SJonathan McDowell #include <linux/mtd/mtd.h> 22d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h> 233d12c0c7SJonathan McDowell #include <linux/mtd/partitions.h> 24*7416bd35SJanusz Krzysztofik #include <linux/platform_device.h> 25fbb080a1SBoris Brezillon #include <linux/sizes.h> 263d12c0c7SJonathan McDowell 273d12c0c7SJonathan McDowell /* 283d12c0c7SJonathan McDowell * MTD structure for E3 (Delta) 293d12c0c7SJonathan McDowell */ 302b44af3aSJanusz Krzysztofik struct ams_delta_nand { 319fd6bcffSBoris Brezillon struct nand_controller base; 322b44af3aSJanusz Krzysztofik struct nand_chip nand_chip; 332b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_rdy; 342b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nce; 352b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nre; 362b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nwp; 372b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nwe; 382b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_ale; 392b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_cle; 40*7416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods; 419c076d7eSJanusz Krzysztofik bool data_in; 422b44af3aSJanusz Krzysztofik }; 433d12c0c7SJonathan McDowell 443d12c0c7SJonathan McDowell /* 453d12c0c7SJonathan McDowell * Define partitions for flash devices 463d12c0c7SJonathan McDowell */ 473d12c0c7SJonathan McDowell 48d4906688SArvind Yadav static const struct mtd_partition partition_info[] = { 493d12c0c7SJonathan McDowell { .name = "Kernel", 503d12c0c7SJonathan McDowell .offset = 0, 513d12c0c7SJonathan McDowell .size = 3 * SZ_1M + SZ_512K }, 523d12c0c7SJonathan McDowell { .name = "u-boot", 533d12c0c7SJonathan McDowell .offset = 3 * SZ_1M + SZ_512K, 543d12c0c7SJonathan McDowell .size = SZ_256K }, 553d12c0c7SJonathan McDowell { .name = "u-boot params", 563d12c0c7SJonathan McDowell .offset = 3 * SZ_1M + SZ_512K + SZ_256K, 573d12c0c7SJonathan McDowell .size = SZ_256K }, 583d12c0c7SJonathan McDowell { .name = "Amstrad LDR", 593d12c0c7SJonathan McDowell .offset = 4 * SZ_1M, 603d12c0c7SJonathan McDowell .size = SZ_256K }, 613d12c0c7SJonathan McDowell { .name = "File system", 623d12c0c7SJonathan McDowell .offset = 4 * SZ_1M + 1 * SZ_256K, 633d12c0c7SJonathan McDowell .size = 27 * SZ_1M }, 643d12c0c7SJonathan McDowell { .name = "PBL reserved", 653d12c0c7SJonathan McDowell .offset = 32 * SZ_1M - 3 * SZ_256K, 663d12c0c7SJonathan McDowell .size = 3 * SZ_256K }, 673d12c0c7SJonathan McDowell }; 683d12c0c7SJonathan McDowell 69*7416bd35SJanusz Krzysztofik static void ams_delta_write_commit(struct ams_delta_nand *priv) 703d12c0c7SJonathan McDowell { 712b44af3aSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nwe, 0); 723d12c0c7SJonathan McDowell ndelay(40); 732b44af3aSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nwe, 1); 743d12c0c7SJonathan McDowell } 753d12c0c7SJonathan McDowell 76*7416bd35SJanusz Krzysztofik static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) 77*7416bd35SJanusz Krzysztofik { 78*7416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods; 79*7416bd35SJanusz Krzysztofik DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; 80*7416bd35SJanusz Krzysztofik 81*7416bd35SJanusz Krzysztofik gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, 82*7416bd35SJanusz Krzysztofik data_gpiods->info, values); 83*7416bd35SJanusz Krzysztofik 84*7416bd35SJanusz Krzysztofik ams_delta_write_commit(priv); 85*7416bd35SJanusz Krzysztofik } 86*7416bd35SJanusz Krzysztofik 87*7416bd35SJanusz Krzysztofik static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte) 88*7416bd35SJanusz Krzysztofik { 89*7416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods; 90*7416bd35SJanusz Krzysztofik DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; 91*7416bd35SJanusz Krzysztofik int i; 92*7416bd35SJanusz Krzysztofik 93*7416bd35SJanusz Krzysztofik for (i = 0; i < data_gpiods->ndescs; i++) 94*7416bd35SJanusz Krzysztofik gpiod_direction_output_raw(data_gpiods->desc[i], 95*7416bd35SJanusz Krzysztofik test_bit(i, values)); 96*7416bd35SJanusz Krzysztofik 97*7416bd35SJanusz Krzysztofik ams_delta_write_commit(priv); 98*7416bd35SJanusz Krzysztofik 99*7416bd35SJanusz Krzysztofik priv->data_in = false; 100*7416bd35SJanusz Krzysztofik } 101*7416bd35SJanusz Krzysztofik 102d54445d6SBoris Brezillon static u8 ams_delta_io_read(struct ams_delta_nand *priv) 1033d12c0c7SJonathan McDowell { 104d54445d6SBoris Brezillon u8 res; 105*7416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods; 106*7416bd35SJanusz Krzysztofik DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; 1073d12c0c7SJonathan McDowell 1082b44af3aSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nre, 0); 1093d12c0c7SJonathan McDowell ndelay(40); 110*7416bd35SJanusz Krzysztofik 111*7416bd35SJanusz Krzysztofik gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, 112*7416bd35SJanusz Krzysztofik data_gpiods->info, values); 113*7416bd35SJanusz Krzysztofik 1142b44af3aSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nre, 1); 1153d12c0c7SJonathan McDowell 116*7416bd35SJanusz Krzysztofik res = values[0]; 1173d12c0c7SJonathan McDowell return res; 1183d12c0c7SJonathan McDowell } 1193d12c0c7SJonathan McDowell 120*7416bd35SJanusz Krzysztofik static void ams_delta_dir_input(struct ams_delta_nand *priv) 1219c076d7eSJanusz Krzysztofik { 122*7416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods; 123*7416bd35SJanusz Krzysztofik int i; 124*7416bd35SJanusz Krzysztofik 125*7416bd35SJanusz Krzysztofik for (i = 0; i < data_gpiods->ndescs; i++) 126*7416bd35SJanusz Krzysztofik gpiod_direction_input(data_gpiods->desc[i]); 127*7416bd35SJanusz Krzysztofik 128*7416bd35SJanusz Krzysztofik priv->data_in = true; 1299c076d7eSJanusz Krzysztofik } 1309c076d7eSJanusz Krzysztofik 131d54445d6SBoris Brezillon static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf, 1323d12c0c7SJonathan McDowell int len) 1333d12c0c7SJonathan McDowell { 134*7416bd35SJanusz Krzysztofik int i = 0; 1353d12c0c7SJonathan McDowell 136*7416bd35SJanusz Krzysztofik if (len > 0 && priv->data_in) 137*7416bd35SJanusz Krzysztofik ams_delta_dir_output(priv, buf[i++]); 1389c076d7eSJanusz Krzysztofik 139*7416bd35SJanusz Krzysztofik while (i < len) 140*7416bd35SJanusz Krzysztofik ams_delta_io_write(priv, buf[i++]); 1413d12c0c7SJonathan McDowell } 1423d12c0c7SJonathan McDowell 143d54445d6SBoris Brezillon static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) 1443d12c0c7SJonathan McDowell { 1453d12c0c7SJonathan McDowell int i; 1463d12c0c7SJonathan McDowell 1479c076d7eSJanusz Krzysztofik if (!priv->data_in) 148*7416bd35SJanusz Krzysztofik ams_delta_dir_input(priv); 1499c076d7eSJanusz Krzysztofik 1503d12c0c7SJonathan McDowell for (i = 0; i < len; i++) 1519c076d7eSJanusz Krzysztofik buf[i] = ams_delta_io_read(priv); 1529c076d7eSJanusz Krzysztofik } 1539c076d7eSJanusz Krzysztofik 1541770022fSBoris Brezillon static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) 1557abd3ef9SThomas Gleixner { 1561770022fSBoris Brezillon gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1); 1577abd3ef9SThomas Gleixner } 1587abd3ef9SThomas Gleixner 159861fbd6eSJanusz Krzysztofik static int ams_delta_exec_op(struct nand_chip *this, 160861fbd6eSJanusz Krzysztofik const struct nand_operation *op, bool check_only) 1613d12c0c7SJonathan McDowell { 1622b44af3aSJanusz Krzysztofik struct ams_delta_nand *priv = nand_get_controller_data(this); 163861fbd6eSJanusz Krzysztofik const struct nand_op_instr *instr; 164861fbd6eSJanusz Krzysztofik int ret = 0; 1652b44af3aSJanusz Krzysztofik 166861fbd6eSJanusz Krzysztofik if (check_only) 167861fbd6eSJanusz Krzysztofik return 0; 168861fbd6eSJanusz Krzysztofik 1691770022fSBoris Brezillon ams_delta_ctrl_cs(priv, 1); 1701770022fSBoris Brezillon 171861fbd6eSJanusz Krzysztofik for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) { 172861fbd6eSJanusz Krzysztofik switch (instr->type) { 173861fbd6eSJanusz Krzysztofik case NAND_OP_CMD_INSTR: 174861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_cle, 1); 175861fbd6eSJanusz Krzysztofik ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1); 176861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_cle, 0); 177861fbd6eSJanusz Krzysztofik break; 178861fbd6eSJanusz Krzysztofik 179861fbd6eSJanusz Krzysztofik case NAND_OP_ADDR_INSTR: 180861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_ale, 1); 181861fbd6eSJanusz Krzysztofik ams_delta_write_buf(priv, instr->ctx.addr.addrs, 182861fbd6eSJanusz Krzysztofik instr->ctx.addr.naddrs); 183861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_ale, 0); 184861fbd6eSJanusz Krzysztofik break; 185861fbd6eSJanusz Krzysztofik 186861fbd6eSJanusz Krzysztofik case NAND_OP_DATA_IN_INSTR: 187861fbd6eSJanusz Krzysztofik ams_delta_read_buf(priv, instr->ctx.data.buf.in, 188861fbd6eSJanusz Krzysztofik instr->ctx.data.len); 189861fbd6eSJanusz Krzysztofik break; 190861fbd6eSJanusz Krzysztofik 191861fbd6eSJanusz Krzysztofik case NAND_OP_DATA_OUT_INSTR: 192861fbd6eSJanusz Krzysztofik ams_delta_write_buf(priv, instr->ctx.data.buf.out, 193861fbd6eSJanusz Krzysztofik instr->ctx.data.len); 194861fbd6eSJanusz Krzysztofik break; 195861fbd6eSJanusz Krzysztofik 196861fbd6eSJanusz Krzysztofik case NAND_OP_WAITRDY_INSTR: 197861fbd6eSJanusz Krzysztofik ret = priv->gpiod_rdy ? 198861fbd6eSJanusz Krzysztofik nand_gpio_waitrdy(this, priv->gpiod_rdy, 199861fbd6eSJanusz Krzysztofik instr->ctx.waitrdy.timeout_ms) : 200861fbd6eSJanusz Krzysztofik nand_soft_waitrdy(this, 201861fbd6eSJanusz Krzysztofik instr->ctx.waitrdy.timeout_ms); 202861fbd6eSJanusz Krzysztofik break; 203861fbd6eSJanusz Krzysztofik } 204861fbd6eSJanusz Krzysztofik 205861fbd6eSJanusz Krzysztofik if (ret) 206861fbd6eSJanusz Krzysztofik break; 207861fbd6eSJanusz Krzysztofik } 208861fbd6eSJanusz Krzysztofik 2091770022fSBoris Brezillon ams_delta_ctrl_cs(priv, 0); 2101770022fSBoris Brezillon 211861fbd6eSJanusz Krzysztofik return ret; 2123d12c0c7SJonathan McDowell } 2133d12c0c7SJonathan McDowell 214f2abfeb2SBoris Brezillon static const struct nand_controller_ops ams_delta_ops = { 215f2abfeb2SBoris Brezillon .exec_op = ams_delta_exec_op, 216f2abfeb2SBoris Brezillon }; 217f2abfeb2SBoris Brezillon 2183d12c0c7SJonathan McDowell /* 2193d12c0c7SJonathan McDowell * Main initialization routine 2203d12c0c7SJonathan McDowell */ 22106f25510SBill Pemberton static int ams_delta_init(struct platform_device *pdev) 2223d12c0c7SJonathan McDowell { 2232b44af3aSJanusz Krzysztofik struct ams_delta_nand *priv; 2243d12c0c7SJonathan McDowell struct nand_chip *this; 2252b44af3aSJanusz Krzysztofik struct mtd_info *mtd; 22697738613SJanusz Krzysztofik struct gpio_descs *data_gpiods; 2273d12c0c7SJonathan McDowell int err = 0; 2283d12c0c7SJonathan McDowell 2293d12c0c7SJonathan McDowell /* Allocate memory for MTD device structure and private data */ 2302b44af3aSJanusz Krzysztofik priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand), 2312b44af3aSJanusz Krzysztofik GFP_KERNEL); 232d54445d6SBoris Brezillon if (!priv) 2332b44af3aSJanusz Krzysztofik return -ENOMEM; 234d54445d6SBoris Brezillon 2352b44af3aSJanusz Krzysztofik this = &priv->nand_chip; 2363d12c0c7SJonathan McDowell 2372b44af3aSJanusz Krzysztofik mtd = nand_to_mtd(this); 2382b44af3aSJanusz Krzysztofik mtd->dev.parent = &pdev->dev; 2393d12c0c7SJonathan McDowell 2402b44af3aSJanusz Krzysztofik nand_set_controller_data(this, priv); 241eaca491fSJanusz Krzysztofik 2422b44af3aSJanusz Krzysztofik priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN); 2432b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_rdy)) { 2442b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_rdy); 245f1a97e0bSJanusz Krzysztofik dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err); 246*7416bd35SJanusz Krzysztofik return err; 2473d12c0c7SJonathan McDowell } 248f1a97e0bSJanusz Krzysztofik 2496dfc6d25SThomas Gleixner this->ecc.mode = NAND_ECC_SOFT; 250e58dd3c3SRafał Miłecki this->ecc.algo = NAND_ECC_HAMMING; 2513d12c0c7SJonathan McDowell 2522b44af3aSJanusz Krzysztofik platform_set_drvdata(pdev, priv); 253eaca491fSJanusz Krzysztofik 2543d12c0c7SJonathan McDowell /* Set chip enabled, but */ 2552b44af3aSJanusz Krzysztofik priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); 2562b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nwp)) { 2572b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nwp); 258f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); 259*7416bd35SJanusz Krzysztofik return err; 260f1a97e0bSJanusz Krzysztofik } 261f1a97e0bSJanusz Krzysztofik 2622b44af3aSJanusz Krzysztofik priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH); 2632b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nce)) { 2642b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nce); 265f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); 266*7416bd35SJanusz Krzysztofik return err; 267f1a97e0bSJanusz Krzysztofik } 268f1a97e0bSJanusz Krzysztofik 2692b44af3aSJanusz Krzysztofik priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH); 2702b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nre)) { 2712b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nre); 272f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); 273*7416bd35SJanusz Krzysztofik return err; 274f1a97e0bSJanusz Krzysztofik } 275f1a97e0bSJanusz Krzysztofik 2762b44af3aSJanusz Krzysztofik priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH); 2772b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nwe)) { 2782b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nwe); 279f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); 280*7416bd35SJanusz Krzysztofik return err; 281f1a97e0bSJanusz Krzysztofik } 282f1a97e0bSJanusz Krzysztofik 2832b44af3aSJanusz Krzysztofik priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW); 2842b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_ale)) { 2852b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_ale); 286f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err); 287*7416bd35SJanusz Krzysztofik return err; 288f1a97e0bSJanusz Krzysztofik } 289f1a97e0bSJanusz Krzysztofik 2902b44af3aSJanusz Krzysztofik priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW); 2912b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_cle)) { 2922b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_cle); 293f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err); 294*7416bd35SJanusz Krzysztofik return err; 295f1a97e0bSJanusz Krzysztofik } 2963d12c0c7SJonathan McDowell 29797738613SJanusz Krzysztofik /* Request array of data pins, initialize them as input */ 29897738613SJanusz Krzysztofik data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); 29997738613SJanusz Krzysztofik if (IS_ERR(data_gpiods)) { 30097738613SJanusz Krzysztofik err = PTR_ERR(data_gpiods); 30197738613SJanusz Krzysztofik dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); 302*7416bd35SJanusz Krzysztofik return err; 30397738613SJanusz Krzysztofik } 304*7416bd35SJanusz Krzysztofik priv->data_gpiods = data_gpiods; 30597738613SJanusz Krzysztofik priv->data_in = true; 3069c076d7eSJanusz Krzysztofik 3079fd6bcffSBoris Brezillon /* Initialize the NAND controller object embedded in ams_delta_nand. */ 308f2abfeb2SBoris Brezillon priv->base.ops = &ams_delta_ops; 3099fd6bcffSBoris Brezillon nand_controller_init(&priv->base); 3109fd6bcffSBoris Brezillon this->controller = &priv->base; 3119fd6bcffSBoris Brezillon 31225985edcSLucas De Marchi /* Scan to find existence of the device */ 31300ad378fSBoris Brezillon err = nand_scan(this, 1); 3140d0aa866SMasahiro Yamada if (err) 315*7416bd35SJanusz Krzysztofik return err; 3163d12c0c7SJonathan McDowell 3173d12c0c7SJonathan McDowell /* Register the partitions */ 318876ba603SBoris Brezillon err = mtd_device_register(mtd, partition_info, 319876ba603SBoris Brezillon ARRAY_SIZE(partition_info)); 320876ba603SBoris Brezillon if (err) 321876ba603SBoris Brezillon goto err_nand_cleanup; 3223d12c0c7SJonathan McDowell 3238bbc3c08SBoris Brezillon return 0; 3243d12c0c7SJonathan McDowell 325876ba603SBoris Brezillon err_nand_cleanup: 326876ba603SBoris Brezillon nand_cleanup(this); 327876ba603SBoris Brezillon 3283d12c0c7SJonathan McDowell return err; 3293d12c0c7SJonathan McDowell } 3303d12c0c7SJonathan McDowell 3313d12c0c7SJonathan McDowell /* 3323d12c0c7SJonathan McDowell * Clean up routine 3333d12c0c7SJonathan McDowell */ 334810b7e06SBill Pemberton static int ams_delta_cleanup(struct platform_device *pdev) 3353d12c0c7SJonathan McDowell { 3362b44af3aSJanusz Krzysztofik struct ams_delta_nand *priv = platform_get_drvdata(pdev); 3372b44af3aSJanusz Krzysztofik struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); 338eaca491fSJanusz Krzysztofik 339*7416bd35SJanusz Krzysztofik /* Unregister device */ 3402b44af3aSJanusz Krzysztofik nand_release(mtd_to_nand(mtd)); 3413d12c0c7SJonathan McDowell 3427e95d1f1SJanusz Krzysztofik return 0; 3433d12c0c7SJonathan McDowell } 3447e95d1f1SJanusz Krzysztofik 3457e95d1f1SJanusz Krzysztofik static struct platform_driver ams_delta_nand_driver = { 3467e95d1f1SJanusz Krzysztofik .probe = ams_delta_init, 3475153b88cSBill Pemberton .remove = ams_delta_cleanup, 3487e95d1f1SJanusz Krzysztofik .driver = { 3497e95d1f1SJanusz Krzysztofik .name = "ams-delta-nand", 3507e95d1f1SJanusz Krzysztofik }, 3517e95d1f1SJanusz Krzysztofik }; 3527e95d1f1SJanusz Krzysztofik 353f99640deSAxel Lin module_platform_driver(ams_delta_nand_driver); 3543d12c0c7SJonathan McDowell 3554857393dSBoris Brezillon MODULE_LICENSE("GPL v2"); 3563d12c0c7SJonathan McDowell MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>"); 3573d12c0c7SJonathan McDowell MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); 358