fsl_upm.c (6653B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Freescale UPM NAND driver. 4 * 5 * Copyright © 2007-2008 MontaVista Software, Inc. 6 * 7 * Author: Anton Vorontsov <avorontsov@ru.mvista.com> 8 */ 9 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/delay.h> 13#include <linux/mtd/rawnand.h> 14#include <linux/mtd/partitions.h> 15#include <linux/mtd/mtd.h> 16#include <linux/of_platform.h> 17#include <linux/io.h> 18#include <linux/slab.h> 19#include <asm/fsl_lbc.h> 20 21struct fsl_upm_nand { 22 struct nand_controller base; 23 struct device *dev; 24 struct nand_chip chip; 25 struct fsl_upm upm; 26 uint8_t upm_addr_offset; 27 uint8_t upm_cmd_offset; 28 void __iomem *io_base; 29 struct gpio_desc *rnb_gpio[NAND_MAX_CHIPS]; 30 uint32_t mchip_offsets[NAND_MAX_CHIPS]; 31 uint32_t mchip_count; 32 uint32_t mchip_number; 33}; 34 35static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) 36{ 37 return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand, 38 chip); 39} 40 41static int fun_chip_init(struct fsl_upm_nand *fun, 42 const struct device_node *upm_np, 43 const struct resource *io_res) 44{ 45 struct mtd_info *mtd = nand_to_mtd(&fun->chip); 46 int ret; 47 struct device_node *flash_np; 48 49 fun->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 50 fun->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; 51 fun->chip.controller = &fun->base; 52 mtd->dev.parent = fun->dev; 53 54 flash_np = of_get_next_child(upm_np, NULL); 55 if (!flash_np) 56 return -ENODEV; 57 58 nand_set_flash_node(&fun->chip, flash_np); 59 mtd->name = devm_kasprintf(fun->dev, GFP_KERNEL, "0x%llx.%pOFn", 60 (u64)io_res->start, 61 flash_np); 62 if (!mtd->name) { 63 ret = -ENOMEM; 64 goto err; 65 } 66 67 ret = nand_scan(&fun->chip, fun->mchip_count); 68 if (ret) 69 goto err; 70 71 ret = mtd_device_register(mtd, NULL, 0); 72err: 73 of_node_put(flash_np); 74 return ret; 75} 76 77static int func_exec_instr(struct nand_chip *chip, 78 const struct nand_op_instr *instr) 79{ 80 struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip)); 81 u32 mar, reg_offs = fun->mchip_offsets[fun->mchip_number]; 82 unsigned int i; 83 const u8 *out; 84 u8 *in; 85 86 switch (instr->type) { 87 case NAND_OP_CMD_INSTR: 88 fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); 89 mar = (instr->ctx.cmd.opcode << (32 - fun->upm.width)) | 90 reg_offs; 91 fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar); 92 fsl_upm_end_pattern(&fun->upm); 93 return 0; 94 95 case NAND_OP_ADDR_INSTR: 96 fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); 97 for (i = 0; i < instr->ctx.addr.naddrs; i++) { 98 mar = (instr->ctx.addr.addrs[i] << (32 - fun->upm.width)) | 99 reg_offs; 100 fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar); 101 } 102 fsl_upm_end_pattern(&fun->upm); 103 return 0; 104 105 case NAND_OP_DATA_IN_INSTR: 106 in = instr->ctx.data.buf.in; 107 for (i = 0; i < instr->ctx.data.len; i++) 108 in[i] = in_8(fun->io_base + reg_offs); 109 return 0; 110 111 case NAND_OP_DATA_OUT_INSTR: 112 out = instr->ctx.data.buf.out; 113 for (i = 0; i < instr->ctx.data.len; i++) 114 out_8(fun->io_base + reg_offs, out[i]); 115 return 0; 116 117 case NAND_OP_WAITRDY_INSTR: 118 if (!fun->rnb_gpio[fun->mchip_number]) 119 return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); 120 121 return nand_gpio_waitrdy(chip, fun->rnb_gpio[fun->mchip_number], 122 instr->ctx.waitrdy.timeout_ms); 123 124 default: 125 return -EINVAL; 126 } 127 128 return 0; 129} 130 131static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op, 132 bool check_only) 133{ 134 struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip)); 135 unsigned int i; 136 int ret; 137 138 if (op->cs > NAND_MAX_CHIPS) 139 return -EINVAL; 140 141 if (check_only) 142 return 0; 143 144 fun->mchip_number = op->cs; 145 146 for (i = 0; i < op->ninstrs; i++) { 147 ret = func_exec_instr(chip, &op->instrs[i]); 148 if (ret) 149 return ret; 150 151 if (op->instrs[i].delay_ns) 152 ndelay(op->instrs[i].delay_ns); 153 } 154 155 return 0; 156} 157 158static const struct nand_controller_ops fun_ops = { 159 .exec_op = fun_exec_op, 160}; 161 162static int fun_probe(struct platform_device *ofdev) 163{ 164 struct fsl_upm_nand *fun; 165 struct resource *io_res; 166 const __be32 *prop; 167 int ret; 168 int size; 169 int i; 170 171 fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL); 172 if (!fun) 173 return -ENOMEM; 174 175 io_res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); 176 fun->io_base = devm_ioremap_resource(&ofdev->dev, io_res); 177 if (IS_ERR(fun->io_base)) 178 return PTR_ERR(fun->io_base); 179 180 ret = fsl_upm_find(io_res->start, &fun->upm); 181 if (ret) { 182 dev_err(&ofdev->dev, "can't find UPM\n"); 183 return ret; 184 } 185 186 prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset", 187 &size); 188 if (!prop || size != sizeof(uint32_t)) { 189 dev_err(&ofdev->dev, "can't get UPM address offset\n"); 190 return -EINVAL; 191 } 192 fun->upm_addr_offset = *prop; 193 194 prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size); 195 if (!prop || size != sizeof(uint32_t)) { 196 dev_err(&ofdev->dev, "can't get UPM command offset\n"); 197 return -EINVAL; 198 } 199 fun->upm_cmd_offset = *prop; 200 201 prop = of_get_property(ofdev->dev.of_node, 202 "fsl,upm-addr-line-cs-offsets", &size); 203 if (prop && (size / sizeof(uint32_t)) > 0) { 204 fun->mchip_count = size / sizeof(uint32_t); 205 if (fun->mchip_count >= NAND_MAX_CHIPS) { 206 dev_err(&ofdev->dev, "too much multiple chips\n"); 207 return -EINVAL; 208 } 209 for (i = 0; i < fun->mchip_count; i++) 210 fun->mchip_offsets[i] = be32_to_cpu(prop[i]); 211 } else { 212 fun->mchip_count = 1; 213 } 214 215 for (i = 0; i < fun->mchip_count; i++) { 216 fun->rnb_gpio[i] = devm_gpiod_get_index_optional(&ofdev->dev, 217 NULL, i, 218 GPIOD_IN); 219 if (IS_ERR(fun->rnb_gpio[i])) { 220 dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); 221 return PTR_ERR(fun->rnb_gpio[i]); 222 } 223 } 224 225 nand_controller_init(&fun->base); 226 fun->base.ops = &fun_ops; 227 fun->dev = &ofdev->dev; 228 229 ret = fun_chip_init(fun, ofdev->dev.of_node, io_res); 230 if (ret) 231 return ret; 232 233 dev_set_drvdata(&ofdev->dev, fun); 234 235 return 0; 236} 237 238static int fun_remove(struct platform_device *ofdev) 239{ 240 struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); 241 struct nand_chip *chip = &fun->chip; 242 struct mtd_info *mtd = nand_to_mtd(chip); 243 int ret; 244 245 ret = mtd_device_unregister(mtd); 246 WARN_ON(ret); 247 nand_cleanup(chip); 248 249 return 0; 250} 251 252static const struct of_device_id of_fun_match[] = { 253 { .compatible = "fsl,upm-nand" }, 254 {}, 255}; 256MODULE_DEVICE_TABLE(of, of_fun_match); 257 258static struct platform_driver of_fun_driver = { 259 .driver = { 260 .name = "fsl,upm-nand", 261 .of_match_table = of_fun_match, 262 }, 263 .probe = fun_probe, 264 .remove = fun_remove, 265}; 266 267module_platform_driver(of_fun_driver); 268 269MODULE_LICENSE("GPL"); 270MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); 271MODULE_DESCRIPTION("Driver for NAND chips working through Freescale " 272 "LocalBus User-Programmable Machine");