1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * NAND Flash Controller Device Driver for DT |
4 | * |
5 | * Copyright © 2011, Picochip. |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/err.h> |
11 | #include <linux/io.h> |
12 | #include <linux/ioport.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/reset.h> |
18 | |
19 | #include "denali.h" |
20 | |
21 | struct denali_dt { |
22 | struct denali_controller controller; |
23 | struct clk *clk; /* core clock */ |
24 | struct clk *clk_x; /* bus interface clock */ |
25 | struct clk *clk_ecc; /* ECC circuit clock */ |
26 | struct reset_control *rst; /* core reset */ |
27 | struct reset_control *rst_reg; /* register reset */ |
28 | }; |
29 | |
30 | struct denali_dt_data { |
31 | unsigned int revision; |
32 | unsigned int caps; |
33 | unsigned int oob_skip_bytes; |
34 | const struct nand_ecc_caps *ecc_caps; |
35 | }; |
36 | |
37 | NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes, |
38 | 512, 8, 15); |
39 | static const struct denali_dt_data denali_socfpga_data = { |
40 | .caps = DENALI_CAP_HW_ECC_FIXUP, |
41 | .oob_skip_bytes = 2, |
42 | .ecc_caps = &denali_socfpga_ecc_caps, |
43 | }; |
44 | |
45 | NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes, |
46 | 1024, 8, 16, 24); |
47 | static const struct denali_dt_data denali_uniphier_v5a_data = { |
48 | .caps = DENALI_CAP_HW_ECC_FIXUP | |
49 | DENALI_CAP_DMA_64BIT, |
50 | .oob_skip_bytes = 8, |
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 | .oob_skip_bytes = 8, |
61 | .ecc_caps = &denali_uniphier_v5b_ecc_caps, |
62 | }; |
63 | |
64 | static const struct of_device_id denali_nand_dt_ids[] = { |
65 | { |
66 | .compatible = "altr,socfpga-denali-nand" , |
67 | .data = &denali_socfpga_data, |
68 | }, |
69 | { |
70 | .compatible = "socionext,uniphier-denali-nand-v5a" , |
71 | .data = &denali_uniphier_v5a_data, |
72 | }, |
73 | { |
74 | .compatible = "socionext,uniphier-denali-nand-v5b" , |
75 | .data = &denali_uniphier_v5b_data, |
76 | }, |
77 | { /* sentinel */ } |
78 | }; |
79 | MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); |
80 | |
81 | static int denali_dt_chip_init(struct denali_controller *denali, |
82 | struct device_node *chip_np) |
83 | { |
84 | struct denali_chip *dchip; |
85 | u32 bank; |
86 | int nsels, i, ret; |
87 | |
88 | nsels = of_property_count_u32_elems(np: chip_np, propname: "reg" ); |
89 | if (nsels < 0) |
90 | return nsels; |
91 | |
92 | dchip = devm_kzalloc(dev: denali->dev, struct_size(dchip, sels, nsels), |
93 | GFP_KERNEL); |
94 | if (!dchip) |
95 | return -ENOMEM; |
96 | |
97 | dchip->nsels = nsels; |
98 | |
99 | for (i = 0; i < nsels; i++) { |
100 | ret = of_property_read_u32_index(np: chip_np, propname: "reg" , index: i, out_value: &bank); |
101 | if (ret) |
102 | return ret; |
103 | |
104 | dchip->sels[i].bank = bank; |
105 | |
106 | nand_set_flash_node(chip: &dchip->chip, np: chip_np); |
107 | } |
108 | |
109 | return denali_chip_init(denali, dchip); |
110 | } |
111 | |
112 | static int denali_dt_probe(struct platform_device *pdev) |
113 | { |
114 | struct device *dev = &pdev->dev; |
115 | struct denali_dt *dt; |
116 | const struct denali_dt_data *data; |
117 | struct denali_controller *denali; |
118 | struct device_node *np; |
119 | int ret; |
120 | |
121 | dt = devm_kzalloc(dev, size: sizeof(*dt), GFP_KERNEL); |
122 | if (!dt) |
123 | return -ENOMEM; |
124 | denali = &dt->controller; |
125 | |
126 | data = of_device_get_match_data(dev); |
127 | if (WARN_ON(!data)) |
128 | return -EINVAL; |
129 | |
130 | denali->revision = data->revision; |
131 | denali->caps = data->caps; |
132 | denali->oob_skip_bytes = data->oob_skip_bytes; |
133 | denali->ecc_caps = data->ecc_caps; |
134 | |
135 | denali->dev = dev; |
136 | denali->irq = platform_get_irq(pdev, 0); |
137 | if (denali->irq < 0) |
138 | return denali->irq; |
139 | |
140 | denali->reg = devm_platform_ioremap_resource_byname(pdev, name: "denali_reg" ); |
141 | if (IS_ERR(ptr: denali->reg)) |
142 | return PTR_ERR(ptr: denali->reg); |
143 | |
144 | denali->host = devm_platform_ioremap_resource_byname(pdev, name: "nand_data" ); |
145 | if (IS_ERR(ptr: denali->host)) |
146 | return PTR_ERR(ptr: denali->host); |
147 | |
148 | dt->clk = devm_clk_get(dev, id: "nand" ); |
149 | if (IS_ERR(ptr: dt->clk)) |
150 | return PTR_ERR(ptr: dt->clk); |
151 | |
152 | dt->clk_x = devm_clk_get(dev, id: "nand_x" ); |
153 | if (IS_ERR(ptr: dt->clk_x)) |
154 | return PTR_ERR(ptr: dt->clk_x); |
155 | |
156 | dt->clk_ecc = devm_clk_get(dev, id: "ecc" ); |
157 | if (IS_ERR(ptr: dt->clk_ecc)) |
158 | return PTR_ERR(ptr: dt->clk_ecc); |
159 | |
160 | dt->rst = devm_reset_control_get_optional_shared(dev, id: "nand" ); |
161 | if (IS_ERR(ptr: dt->rst)) |
162 | return PTR_ERR(ptr: dt->rst); |
163 | |
164 | dt->rst_reg = devm_reset_control_get_optional_shared(dev, id: "reg" ); |
165 | if (IS_ERR(ptr: dt->rst_reg)) |
166 | return PTR_ERR(ptr: dt->rst_reg); |
167 | |
168 | ret = clk_prepare_enable(clk: dt->clk); |
169 | if (ret) |
170 | return ret; |
171 | |
172 | ret = clk_prepare_enable(clk: dt->clk_x); |
173 | if (ret) |
174 | goto out_disable_clk; |
175 | |
176 | ret = clk_prepare_enable(clk: dt->clk_ecc); |
177 | if (ret) |
178 | goto out_disable_clk_x; |
179 | |
180 | denali->clk_rate = clk_get_rate(clk: dt->clk); |
181 | denali->clk_x_rate = clk_get_rate(clk: dt->clk_x); |
182 | |
183 | /* |
184 | * Deassert the register reset, and the core reset in this order. |
185 | * Deasserting the core reset while the register reset is asserted |
186 | * will cause unpredictable behavior in the controller. |
187 | */ |
188 | ret = reset_control_deassert(rstc: dt->rst_reg); |
189 | if (ret) |
190 | goto out_disable_clk_ecc; |
191 | |
192 | ret = reset_control_deassert(rstc: dt->rst); |
193 | if (ret) |
194 | goto out_assert_rst_reg; |
195 | |
196 | /* |
197 | * When the reset is deasserted, the initialization sequence is kicked |
198 | * (bootstrap process). The driver must wait until it finished. |
199 | * Otherwise, it will result in unpredictable behavior. |
200 | */ |
201 | usleep_range(min: 200, max: 1000); |
202 | |
203 | ret = denali_init(denali); |
204 | if (ret) |
205 | goto out_assert_rst; |
206 | |
207 | for_each_child_of_node(dev->of_node, np) { |
208 | ret = denali_dt_chip_init(denali, chip_np: np); |
209 | if (ret) { |
210 | of_node_put(node: np); |
211 | goto out_remove_denali; |
212 | } |
213 | } |
214 | |
215 | platform_set_drvdata(pdev, data: dt); |
216 | |
217 | return 0; |
218 | |
219 | out_remove_denali: |
220 | denali_remove(denali); |
221 | out_assert_rst: |
222 | reset_control_assert(rstc: dt->rst); |
223 | out_assert_rst_reg: |
224 | reset_control_assert(rstc: dt->rst_reg); |
225 | out_disable_clk_ecc: |
226 | clk_disable_unprepare(clk: dt->clk_ecc); |
227 | out_disable_clk_x: |
228 | clk_disable_unprepare(clk: dt->clk_x); |
229 | out_disable_clk: |
230 | clk_disable_unprepare(clk: dt->clk); |
231 | |
232 | return ret; |
233 | } |
234 | |
235 | static void denali_dt_remove(struct platform_device *pdev) |
236 | { |
237 | struct denali_dt *dt = platform_get_drvdata(pdev); |
238 | |
239 | denali_remove(denali: &dt->controller); |
240 | reset_control_assert(rstc: dt->rst); |
241 | reset_control_assert(rstc: dt->rst_reg); |
242 | clk_disable_unprepare(clk: dt->clk_ecc); |
243 | clk_disable_unprepare(clk: dt->clk_x); |
244 | clk_disable_unprepare(clk: dt->clk); |
245 | } |
246 | |
247 | static struct platform_driver denali_dt_driver = { |
248 | .probe = denali_dt_probe, |
249 | .remove_new = denali_dt_remove, |
250 | .driver = { |
251 | .name = "denali-nand-dt" , |
252 | .of_match_table = denali_nand_dt_ids, |
253 | }, |
254 | }; |
255 | module_platform_driver(denali_dt_driver); |
256 | |
257 | MODULE_LICENSE("GPL v2" ); |
258 | MODULE_AUTHOR("Jamie Iles" ); |
259 | MODULE_DESCRIPTION("DT driver for Denali NAND controller" ); |
260 | |