cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");