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;	/* core clock */
31 	struct clk *clk_x;	/* bus interface clock */
32 	struct clk *clk_ecc;	/* ECC circuit clock */
33 };
34 
35 struct denali_dt_data {
36 	unsigned int revision;
37 	unsigned int caps;
38 	const struct nand_ecc_caps *ecc_caps;
39 };
40 
41 NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
42 		     512, 8, 15);
43 static const struct denali_dt_data denali_socfpga_data = {
44 	.caps = DENALI_CAP_HW_ECC_FIXUP,
45 	.ecc_caps = &denali_socfpga_ecc_caps,
46 };
47 
48 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
49 		     1024, 8, 16, 24);
50 static const struct denali_dt_data denali_uniphier_v5a_data = {
51 	.caps = DENALI_CAP_HW_ECC_FIXUP |
52 		DENALI_CAP_DMA_64BIT,
53 	.ecc_caps = &denali_uniphier_v5a_ecc_caps,
54 };
55 
56 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
57 		     1024, 8, 16);
58 static const struct denali_dt_data denali_uniphier_v5b_data = {
59 	.revision = 0x0501,
60 	.caps = DENALI_CAP_HW_ECC_FIXUP |
61 		DENALI_CAP_DMA_64BIT,
62 	.ecc_caps = &denali_uniphier_v5b_ecc_caps,
63 };
64 
65 static const struct of_device_id denali_nand_dt_ids[] = {
66 	{
67 		.compatible = "altr,socfpga-denali-nand",
68 		.data = &denali_socfpga_data,
69 	},
70 	{
71 		.compatible = "socionext,uniphier-denali-nand-v5a",
72 		.data = &denali_uniphier_v5a_data,
73 	},
74 	{
75 		.compatible = "socionext,uniphier-denali-nand-v5b",
76 		.data = &denali_uniphier_v5b_data,
77 	},
78 	{ /* sentinel */ }
79 };
80 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
81 
denali_dt_probe(struct platform_device * pdev)82 static int denali_dt_probe(struct platform_device *pdev)
83 {
84 	struct device *dev = &pdev->dev;
85 	struct resource *res;
86 	struct denali_dt *dt;
87 	const struct denali_dt_data *data;
88 	struct denali_nand_info *denali;
89 	int ret;
90 
91 	dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
92 	if (!dt)
93 		return -ENOMEM;
94 	denali = &dt->denali;
95 
96 	data = of_device_get_match_data(dev);
97 	if (data) {
98 		denali->revision = data->revision;
99 		denali->caps = data->caps;
100 		denali->ecc_caps = data->ecc_caps;
101 	}
102 
103 	denali->dev = dev;
104 	denali->irq = platform_get_irq(pdev, 0);
105 	if (denali->irq < 0) {
106 		dev_err(dev, "no irq defined\n");
107 		return denali->irq;
108 	}
109 
110 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
111 	denali->reg = devm_ioremap_resource(dev, res);
112 	if (IS_ERR(denali->reg))
113 		return PTR_ERR(denali->reg);
114 
115 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
116 	denali->host = devm_ioremap_resource(dev, res);
117 	if (IS_ERR(denali->host))
118 		return PTR_ERR(denali->host);
119 
120 	/*
121 	 * A single anonymous clock is supported for the backward compatibility.
122 	 * New platforms should support all the named clocks.
123 	 */
124 	dt->clk = devm_clk_get(dev, "nand");
125 	if (IS_ERR(dt->clk))
126 		dt->clk = devm_clk_get(dev, NULL);
127 	if (IS_ERR(dt->clk)) {
128 		dev_err(dev, "no clk available\n");
129 		return PTR_ERR(dt->clk);
130 	}
131 
132 	dt->clk_x = devm_clk_get(dev, "nand_x");
133 	if (IS_ERR(dt->clk_x))
134 		dt->clk_x = NULL;
135 
136 	dt->clk_ecc = devm_clk_get(dev, "ecc");
137 	if (IS_ERR(dt->clk_ecc))
138 		dt->clk_ecc = NULL;
139 
140 	ret = clk_prepare_enable(dt->clk);
141 	if (ret)
142 		return ret;
143 
144 	ret = clk_prepare_enable(dt->clk_x);
145 	if (ret)
146 		goto out_disable_clk;
147 
148 	ret = clk_prepare_enable(dt->clk_ecc);
149 	if (ret)
150 		goto out_disable_clk_x;
151 
152 	if (dt->clk_x) {
153 		denali->clk_rate = clk_get_rate(dt->clk);
154 		denali->clk_x_rate = clk_get_rate(dt->clk_x);
155 	} else {
156 		/*
157 		 * Hardcode the clock rates for the backward compatibility.
158 		 * This works for both SOCFPGA and UniPhier.
159 		 */
160 		dev_notice(dev,
161 			   "necessary clock is missing. default clock rates are used.\n");
162 		denali->clk_rate = 50000000;
163 		denali->clk_x_rate = 200000000;
164 	}
165 
166 	ret = denali_init(denali);
167 	if (ret)
168 		goto out_disable_clk_ecc;
169 
170 	platform_set_drvdata(pdev, dt);
171 	return 0;
172 
173 out_disable_clk_ecc:
174 	clk_disable_unprepare(dt->clk_ecc);
175 out_disable_clk_x:
176 	clk_disable_unprepare(dt->clk_x);
177 out_disable_clk:
178 	clk_disable_unprepare(dt->clk);
179 
180 	return ret;
181 }
182 
denali_dt_remove(struct platform_device * pdev)183 static int denali_dt_remove(struct platform_device *pdev)
184 {
185 	struct denali_dt *dt = platform_get_drvdata(pdev);
186 
187 	denali_remove(&dt->denali);
188 	clk_disable_unprepare(dt->clk_ecc);
189 	clk_disable_unprepare(dt->clk_x);
190 	clk_disable_unprepare(dt->clk);
191 
192 	return 0;
193 }
194 
195 static struct platform_driver denali_dt_driver = {
196 	.probe		= denali_dt_probe,
197 	.remove		= denali_dt_remove,
198 	.driver		= {
199 		.name	= "denali-nand-dt",
200 		.of_match_table	= denali_nand_dt_ids,
201 	},
202 };
203 module_platform_driver(denali_dt_driver);
204 
205 MODULE_LICENSE("GPL");
206 MODULE_AUTHOR("Jamie Iles");
207 MODULE_DESCRIPTION("DT driver for Denali NAND controller");
208