Merge branch 'work.mkdir' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[sfrench/cifs-2.6.git] / drivers / mtd / nand / raw / denali_dt.c
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
16 #include <linux/clk.h>
17 #include <linux/err.h>
18 #include <linux/io.h>
19 #include <linux/ioport.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/of.h>
23 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25
26 #include "denali.h"
27
28 struct denali_dt {
29         struct denali_nand_info denali;
30         struct clk              *clk;
31 };
32
33 struct denali_dt_data {
34         unsigned int revision;
35         unsigned int caps;
36         const struct nand_ecc_caps *ecc_caps;
37 };
38
39 NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
40                      512, 8, 15);
41 static const struct denali_dt_data denali_socfpga_data = {
42         .caps = DENALI_CAP_HW_ECC_FIXUP,
43         .ecc_caps = &denali_socfpga_ecc_caps,
44 };
45
46 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
47                      1024, 8, 16, 24);
48 static const struct denali_dt_data denali_uniphier_v5a_data = {
49         .caps = DENALI_CAP_HW_ECC_FIXUP |
50                 DENALI_CAP_DMA_64BIT,
51         .ecc_caps = &denali_uniphier_v5a_ecc_caps,
52 };
53
54 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
55                      1024, 8, 16);
56 static const struct denali_dt_data denali_uniphier_v5b_data = {
57         .revision = 0x0501,
58         .caps = DENALI_CAP_HW_ECC_FIXUP |
59                 DENALI_CAP_DMA_64BIT,
60         .ecc_caps = &denali_uniphier_v5b_ecc_caps,
61 };
62
63 static const struct of_device_id denali_nand_dt_ids[] = {
64         {
65                 .compatible = "altr,socfpga-denali-nand",
66                 .data = &denali_socfpga_data,
67         },
68         {
69                 .compatible = "socionext,uniphier-denali-nand-v5a",
70                 .data = &denali_uniphier_v5a_data,
71         },
72         {
73                 .compatible = "socionext,uniphier-denali-nand-v5b",
74                 .data = &denali_uniphier_v5b_data,
75         },
76         { /* sentinel */ }
77 };
78 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
79
80 static int denali_dt_probe(struct platform_device *pdev)
81 {
82         struct resource *res;
83         struct denali_dt *dt;
84         const struct denali_dt_data *data;
85         struct denali_nand_info *denali;
86         int ret;
87
88         dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
89         if (!dt)
90                 return -ENOMEM;
91         denali = &dt->denali;
92
93         data = of_device_get_match_data(&pdev->dev);
94         if (data) {
95                 denali->revision = data->revision;
96                 denali->caps = data->caps;
97                 denali->ecc_caps = data->ecc_caps;
98         }
99
100         denali->dev = &pdev->dev;
101         denali->irq = platform_get_irq(pdev, 0);
102         if (denali->irq < 0) {
103                 dev_err(&pdev->dev, "no irq defined\n");
104                 return denali->irq;
105         }
106
107         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
108         denali->reg = devm_ioremap_resource(&pdev->dev, res);
109         if (IS_ERR(denali->reg))
110                 return PTR_ERR(denali->reg);
111
112         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
113         denali->host = devm_ioremap_resource(&pdev->dev, res);
114         if (IS_ERR(denali->host))
115                 return PTR_ERR(denali->host);
116
117         dt->clk = devm_clk_get(&pdev->dev, NULL);
118         if (IS_ERR(dt->clk)) {
119                 dev_err(&pdev->dev, "no clk available\n");
120                 return PTR_ERR(dt->clk);
121         }
122         ret = clk_prepare_enable(dt->clk);
123         if (ret)
124                 return ret;
125
126         /*
127          * Hardcode the clock rate for the backward compatibility.
128          * This works for both SOCFPGA and UniPhier.
129          */
130         denali->clk_x_rate = 200000000;
131
132         ret = denali_init(denali);
133         if (ret)
134                 goto out_disable_clk;
135
136         platform_set_drvdata(pdev, dt);
137         return 0;
138
139 out_disable_clk:
140         clk_disable_unprepare(dt->clk);
141
142         return ret;
143 }
144
145 static int denali_dt_remove(struct platform_device *pdev)
146 {
147         struct denali_dt *dt = platform_get_drvdata(pdev);
148
149         denali_remove(&dt->denali);
150         clk_disable_unprepare(dt->clk);
151
152         return 0;
153 }
154
155 static struct platform_driver denali_dt_driver = {
156         .probe          = denali_dt_probe,
157         .remove         = denali_dt_remove,
158         .driver         = {
159                 .name   = "denali-nand-dt",
160                 .of_match_table = denali_nand_dt_ids,
161         },
162 };
163 module_platform_driver(denali_dt_driver);
164
165 MODULE_LICENSE("GPL");
166 MODULE_AUTHOR("Jamie Iles");
167 MODULE_DESCRIPTION("DT driver for Denali NAND controller");