linux/drivers/mtd/nand/denali_dt.c
<<
>>
Prefs
   1/*
   2 * NAND Flash Controller Device Driver for DT
   3 *
   4 * Copyright © 2011, Picochip.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 */
  15#include <linux/clk.h>
  16#include <linux/err.h>
  17#include <linux/io.h>
  18#include <linux/ioport.h>
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22#include <linux/of.h>
  23#include <linux/of_device.h>
  24
  25#include "denali.h"
  26
  27struct denali_dt {
  28        struct denali_nand_info denali;
  29        struct clk              *clk;
  30};
  31
  32struct denali_dt_data {
  33        unsigned int revision;
  34        unsigned int caps;
  35        const struct nand_ecc_caps *ecc_caps;
  36};
  37
  38NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
  39                     512, 8, 15);
  40static const struct denali_dt_data denali_socfpga_data = {
  41        .caps = DENALI_CAP_HW_ECC_FIXUP,
  42        .ecc_caps = &denali_socfpga_ecc_caps,
  43};
  44
  45NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
  46                     1024, 8, 16, 24);
  47static const struct denali_dt_data denali_uniphier_v5a_data = {
  48        .caps = DENALI_CAP_HW_ECC_FIXUP |
  49                DENALI_CAP_DMA_64BIT,
  50        .ecc_caps = &denali_uniphier_v5a_ecc_caps,
  51};
  52
  53NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
  54                     1024, 8, 16);
  55static const struct denali_dt_data denali_uniphier_v5b_data = {
  56        .revision = 0x0501,
  57        .caps = DENALI_CAP_HW_ECC_FIXUP |
  58                DENALI_CAP_DMA_64BIT,
  59        .ecc_caps = &denali_uniphier_v5b_ecc_caps,
  60};
  61
  62static const struct of_device_id denali_nand_dt_ids[] = {
  63        {
  64                .compatible = "altr,socfpga-denali-nand",
  65                .data = &denali_socfpga_data,
  66        },
  67        {
  68                .compatible = "socionext,uniphier-denali-nand-v5a",
  69                .data = &denali_uniphier_v5a_data,
  70        },
  71        {
  72                .compatible = "socionext,uniphier-denali-nand-v5b",
  73                .data = &denali_uniphier_v5b_data,
  74        },
  75        { /* sentinel */ }
  76};
  77MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
  78
  79static int denali_dt_probe(struct platform_device *pdev)
  80{
  81        struct resource *res;
  82        struct denali_dt *dt;
  83        const struct denali_dt_data *data;
  84        struct denali_nand_info *denali;
  85        int ret;
  86
  87        dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
  88        if (!dt)
  89                return -ENOMEM;
  90        denali = &dt->denali;
  91
  92        data = of_device_get_match_data(&pdev->dev);
  93        if (data) {
  94                denali->revision = data->revision;
  95                denali->caps = data->caps;
  96                denali->ecc_caps = data->ecc_caps;
  97        }
  98
  99        denali->dev = &pdev->dev;
 100        denali->irq = platform_get_irq(pdev, 0);
 101        if (denali->irq < 0) {
 102                dev_err(&pdev->dev, "no irq defined\n");
 103                return denali->irq;
 104        }
 105
 106        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
 107        denali->reg = devm_ioremap_resource(&pdev->dev, res);
 108        if (IS_ERR(denali->reg))
 109                return PTR_ERR(denali->reg);
 110
 111        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
 112        denali->host = devm_ioremap_resource(&pdev->dev, res);
 113        if (IS_ERR(denali->host))
 114                return PTR_ERR(denali->host);
 115
 116        dt->clk = devm_clk_get(&pdev->dev, NULL);
 117        if (IS_ERR(dt->clk)) {
 118                dev_err(&pdev->dev, "no clk available\n");
 119                return PTR_ERR(dt->clk);
 120        }
 121        clk_prepare_enable(dt->clk);
 122
 123        denali->clk_x_rate = clk_get_rate(dt->clk);
 124
 125        ret = denali_init(denali);
 126        if (ret)
 127                goto out_disable_clk;
 128
 129        platform_set_drvdata(pdev, dt);
 130        return 0;
 131
 132out_disable_clk:
 133        clk_disable_unprepare(dt->clk);
 134
 135        return ret;
 136}
 137
 138static int denali_dt_remove(struct platform_device *pdev)
 139{
 140        struct denali_dt *dt = platform_get_drvdata(pdev);
 141
 142        denali_remove(&dt->denali);
 143        clk_disable_unprepare(dt->clk);
 144
 145        return 0;
 146}
 147
 148static struct platform_driver denali_dt_driver = {
 149        .probe          = denali_dt_probe,
 150        .remove         = denali_dt_remove,
 151        .driver         = {
 152                .name   = "denali-nand-dt",
 153                .of_match_table = denali_nand_dt_ids,
 154        },
 155};
 156
 157module_platform_driver(denali_dt_driver);
 158
 159MODULE_LICENSE("GPL");
 160MODULE_AUTHOR("Jamie Iles");
 161MODULE_DESCRIPTION("DT driver for Denali NAND controller");
 162