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

ndfc.c (6819B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Overview:
      4 *   Platform independent driver for NDFC (NanD Flash Controller)
      5 *   integrated into EP440 cores
      6 *
      7 *   Ported to an OF platform driver by Sean MacLennan
      8 *
      9 *   The NDFC supports multiple chips, but this driver only supports a
     10 *   single chip since I do not have access to any boards with
     11 *   multiple chips.
     12 *
     13 *  Author: Thomas Gleixner
     14 *
     15 *  Copyright 2006 IBM
     16 *  Copyright 2008 PIKA Technologies
     17 *    Sean MacLennan <smaclennan@pikatech.com>
     18 */
     19#include <linux/module.h>
     20#include <linux/mtd/rawnand.h>
     21#include <linux/mtd/partitions.h>
     22#include <linux/mtd/ndfc.h>
     23#include <linux/slab.h>
     24#include <linux/mtd/mtd.h>
     25#include <linux/of_address.h>
     26#include <linux/of_platform.h>
     27#include <asm/io.h>
     28
     29#define NDFC_MAX_CS    4
     30
     31struct ndfc_controller {
     32	struct platform_device *ofdev;
     33	void __iomem *ndfcbase;
     34	struct nand_chip chip;
     35	int chip_select;
     36	struct nand_controller ndfc_control;
     37};
     38
     39static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS];
     40
     41static void ndfc_select_chip(struct nand_chip *nchip, int chip)
     42{
     43	uint32_t ccr;
     44	struct ndfc_controller *ndfc = nand_get_controller_data(nchip);
     45
     46	ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
     47	if (chip >= 0) {
     48		ccr &= ~NDFC_CCR_BS_MASK;
     49		ccr |= NDFC_CCR_BS(chip + ndfc->chip_select);
     50	} else
     51		ccr |= NDFC_CCR_RESET_CE;
     52	out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
     53}
     54
     55static void ndfc_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl)
     56{
     57	struct ndfc_controller *ndfc = nand_get_controller_data(chip);
     58
     59	if (cmd == NAND_CMD_NONE)
     60		return;
     61
     62	if (ctrl & NAND_CLE)
     63		writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD);
     64	else
     65		writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE);
     66}
     67
     68static int ndfc_ready(struct nand_chip *chip)
     69{
     70	struct ndfc_controller *ndfc = nand_get_controller_data(chip);
     71
     72	return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
     73}
     74
     75static void ndfc_enable_hwecc(struct nand_chip *chip, int mode)
     76{
     77	uint32_t ccr;
     78	struct ndfc_controller *ndfc = nand_get_controller_data(chip);
     79
     80	ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
     81	ccr |= NDFC_CCR_RESET_ECC;
     82	out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
     83	wmb();
     84}
     85
     86static int ndfc_calculate_ecc(struct nand_chip *chip,
     87			      const u_char *dat, u_char *ecc_code)
     88{
     89	struct ndfc_controller *ndfc = nand_get_controller_data(chip);
     90	uint32_t ecc;
     91	uint8_t *p = (uint8_t *)&ecc;
     92
     93	wmb();
     94	ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
     95	/* The NDFC uses Smart Media (SMC) bytes order */
     96	ecc_code[0] = p[1];
     97	ecc_code[1] = p[2];
     98	ecc_code[2] = p[3];
     99
    100	return 0;
    101}
    102
    103/*
    104 * Speedups for buffer read/write/verify
    105 *
    106 * NDFC allows 32bit read/write of data. So we can speed up the buffer
    107 * functions. No further checking, as nand_base will always read/write
    108 * page aligned.
    109 */
    110static void ndfc_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
    111{
    112	struct ndfc_controller *ndfc = nand_get_controller_data(chip);
    113	uint32_t *p = (uint32_t *) buf;
    114
    115	for(;len > 0; len -= 4)
    116		*p++ = in_be32(ndfc->ndfcbase + NDFC_DATA);
    117}
    118
    119static void ndfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
    120{
    121	struct ndfc_controller *ndfc = nand_get_controller_data(chip);
    122	uint32_t *p = (uint32_t *) buf;
    123
    124	for(;len > 0; len -= 4)
    125		out_be32(ndfc->ndfcbase + NDFC_DATA, *p++);
    126}
    127
    128/*
    129 * Initialize chip structure
    130 */
    131static int ndfc_chip_init(struct ndfc_controller *ndfc,
    132			  struct device_node *node)
    133{
    134	struct device_node *flash_np;
    135	struct nand_chip *chip = &ndfc->chip;
    136	struct mtd_info *mtd = nand_to_mtd(chip);
    137	int ret;
    138
    139	chip->legacy.IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
    140	chip->legacy.IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
    141	chip->legacy.cmd_ctrl = ndfc_hwcontrol;
    142	chip->legacy.dev_ready = ndfc_ready;
    143	chip->legacy.select_chip = ndfc_select_chip;
    144	chip->legacy.chip_delay = 50;
    145	chip->controller = &ndfc->ndfc_control;
    146	chip->legacy.read_buf = ndfc_read_buf;
    147	chip->legacy.write_buf = ndfc_write_buf;
    148	chip->ecc.correct = rawnand_sw_hamming_correct;
    149	chip->ecc.hwctl = ndfc_enable_hwecc;
    150	chip->ecc.calculate = ndfc_calculate_ecc;
    151	chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
    152	chip->ecc.size = 256;
    153	chip->ecc.bytes = 3;
    154	chip->ecc.strength = 1;
    155	nand_set_controller_data(chip, ndfc);
    156
    157	mtd->dev.parent = &ndfc->ofdev->dev;
    158
    159	flash_np = of_get_next_child(node, NULL);
    160	if (!flash_np)
    161		return -ENODEV;
    162	nand_set_flash_node(chip, flash_np);
    163
    164	mtd->name = kasprintf(GFP_KERNEL, "%s.%pOFn", dev_name(&ndfc->ofdev->dev),
    165			      flash_np);
    166	if (!mtd->name) {
    167		ret = -ENOMEM;
    168		goto err;
    169	}
    170
    171	ret = nand_scan(chip, 1);
    172	if (ret)
    173		goto err;
    174
    175	ret = mtd_device_register(mtd, NULL, 0);
    176
    177err:
    178	of_node_put(flash_np);
    179	if (ret)
    180		kfree(mtd->name);
    181	return ret;
    182}
    183
    184static int ndfc_probe(struct platform_device *ofdev)
    185{
    186	struct ndfc_controller *ndfc;
    187	const __be32 *reg;
    188	u32 ccr;
    189	u32 cs;
    190	int err, len;
    191
    192	/* Read the reg property to get the chip select */
    193	reg = of_get_property(ofdev->dev.of_node, "reg", &len);
    194	if (reg == NULL || len != 12) {
    195		dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
    196		return -ENOENT;
    197	}
    198
    199	cs = be32_to_cpu(reg[0]);
    200	if (cs >= NDFC_MAX_CS) {
    201		dev_err(&ofdev->dev, "invalid CS number (%d)\n", cs);
    202		return -EINVAL;
    203	}
    204
    205	ndfc = &ndfc_ctrl[cs];
    206	ndfc->chip_select = cs;
    207
    208	nand_controller_init(&ndfc->ndfc_control);
    209	ndfc->ofdev = ofdev;
    210	dev_set_drvdata(&ofdev->dev, ndfc);
    211
    212	ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0);
    213	if (!ndfc->ndfcbase) {
    214		dev_err(&ofdev->dev, "failed to get memory\n");
    215		return -EIO;
    216	}
    217
    218	ccr = NDFC_CCR_BS(ndfc->chip_select);
    219
    220	/* It is ok if ccr does not exist - just default to 0 */
    221	reg = of_get_property(ofdev->dev.of_node, "ccr", NULL);
    222	if (reg)
    223		ccr |= be32_to_cpup(reg);
    224
    225	out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
    226
    227	/* Set the bank settings if given */
    228	reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL);
    229	if (reg) {
    230		int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
    231		out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg));
    232	}
    233
    234	err = ndfc_chip_init(ndfc, ofdev->dev.of_node);
    235	if (err) {
    236		iounmap(ndfc->ndfcbase);
    237		return err;
    238	}
    239
    240	return 0;
    241}
    242
    243static int ndfc_remove(struct platform_device *ofdev)
    244{
    245	struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
    246	struct nand_chip *chip = &ndfc->chip;
    247	struct mtd_info *mtd = nand_to_mtd(chip);
    248	int ret;
    249
    250	ret = mtd_device_unregister(mtd);
    251	WARN_ON(ret);
    252	nand_cleanup(chip);
    253	kfree(mtd->name);
    254
    255	return 0;
    256}
    257
    258static const struct of_device_id ndfc_match[] = {
    259	{ .compatible = "ibm,ndfc", },
    260	{}
    261};
    262MODULE_DEVICE_TABLE(of, ndfc_match);
    263
    264static struct platform_driver ndfc_driver = {
    265	.driver = {
    266		.name = "ndfc",
    267		.of_match_table = ndfc_match,
    268	},
    269	.probe = ndfc_probe,
    270	.remove = ndfc_remove,
    271};
    272
    273module_platform_driver(ndfc_driver);
    274
    275MODULE_LICENSE("GPL");
    276MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
    277MODULE_DESCRIPTION("OF Platform driver for NDFC");