diff options
Diffstat (limited to 'drivers/memory/jz4780-nemc.c')
-rw-r--r-- | drivers/memory/jz4780-nemc.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c new file mode 100644 index 000000000000..919d1925acb9 --- /dev/null +++ b/drivers/memory/jz4780-nemc.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | * JZ4780 NAND/external memory controller (NEMC) | ||
3 | * | ||
4 | * Copyright (c) 2015 Imagination Technologies | ||
5 | * Author: Alex Smith <alex@alex-smith.me.uk> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published | ||
9 | * by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/math64.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/of_device.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | |||
23 | #include <linux/jz4780-nemc.h> | ||
24 | |||
25 | #define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4)) | ||
26 | #define NEMC_NFCSR 0x50 | ||
27 | |||
28 | #define NEMC_SMCR_SMT BIT(0) | ||
29 | #define NEMC_SMCR_BW_SHIFT 6 | ||
30 | #define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT) | ||
31 | #define NEMC_SMCR_BW_8 (0 << 6) | ||
32 | #define NEMC_SMCR_TAS_SHIFT 8 | ||
33 | #define NEMC_SMCR_TAS_MASK (0xf << NEMC_SMCR_TAS_SHIFT) | ||
34 | #define NEMC_SMCR_TAH_SHIFT 12 | ||
35 | #define NEMC_SMCR_TAH_MASK (0xf << NEMC_SMCR_TAH_SHIFT) | ||
36 | #define NEMC_SMCR_TBP_SHIFT 16 | ||
37 | #define NEMC_SMCR_TBP_MASK (0xf << NEMC_SMCR_TBP_SHIFT) | ||
38 | #define NEMC_SMCR_TAW_SHIFT 20 | ||
39 | #define NEMC_SMCR_TAW_MASK (0xf << NEMC_SMCR_TAW_SHIFT) | ||
40 | #define NEMC_SMCR_TSTRV_SHIFT 24 | ||
41 | #define NEMC_SMCR_TSTRV_MASK (0x3f << NEMC_SMCR_TSTRV_SHIFT) | ||
42 | |||
43 | #define NEMC_NFCSR_NFEn(n) BIT(((n) - 1) << 1) | ||
44 | #define NEMC_NFCSR_NFCEn(n) BIT((((n) - 1) << 1) + 1) | ||
45 | #define NEMC_NFCSR_TNFEn(n) BIT(16 + (n) - 1) | ||
46 | |||
47 | struct jz4780_nemc { | ||
48 | spinlock_t lock; | ||
49 | struct device *dev; | ||
50 | void __iomem *base; | ||
51 | struct clk *clk; | ||
52 | uint32_t clk_period; | ||
53 | unsigned long banks_present; | ||
54 | }; | ||
55 | |||
56 | /** | ||
57 | * jz4780_nemc_num_banks() - count the number of banks referenced by a device | ||
58 | * @dev: device to count banks for, must be a child of the NEMC. | ||
59 | * | ||
60 | * Return: The number of unique NEMC banks referred to by the specified NEMC | ||
61 | * child device. Unique here means that a device that references the same bank | ||
62 | * multiple times in the its "reg" property will only count once. | ||
63 | */ | ||
64 | unsigned int jz4780_nemc_num_banks(struct device *dev) | ||
65 | { | ||
66 | const __be32 *prop; | ||
67 | unsigned int bank, count = 0; | ||
68 | unsigned long referenced = 0; | ||
69 | int i = 0; | ||
70 | |||
71 | while ((prop = of_get_address(dev->of_node, i++, NULL, NULL))) { | ||
72 | bank = of_read_number(prop, 1); | ||
73 | if (!(referenced & BIT(bank))) { | ||
74 | referenced |= BIT(bank); | ||
75 | count++; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | return count; | ||
80 | } | ||
81 | EXPORT_SYMBOL(jz4780_nemc_num_banks); | ||
82 | |||
83 | /** | ||
84 | * jz4780_nemc_set_type() - set the type of device connected to a bank | ||
85 | * @dev: child device of the NEMC. | ||
86 | * @bank: bank number to configure. | ||
87 | * @type: type of device connected to the bank. | ||
88 | */ | ||
89 | void jz4780_nemc_set_type(struct device *dev, unsigned int bank, | ||
90 | enum jz4780_nemc_bank_type type) | ||
91 | { | ||
92 | struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent); | ||
93 | uint32_t nfcsr; | ||
94 | |||
95 | nfcsr = readl(nemc->base + NEMC_NFCSR); | ||
96 | |||
97 | /* TODO: Support toggle NAND devices. */ | ||
98 | switch (type) { | ||
99 | case JZ4780_NEMC_BANK_SRAM: | ||
100 | nfcsr &= ~(NEMC_NFCSR_TNFEn(bank) | NEMC_NFCSR_NFEn(bank)); | ||
101 | break; | ||
102 | case JZ4780_NEMC_BANK_NAND: | ||
103 | nfcsr &= ~NEMC_NFCSR_TNFEn(bank); | ||
104 | nfcsr |= NEMC_NFCSR_NFEn(bank); | ||
105 | break; | ||
106 | } | ||
107 | |||
108 | writel(nfcsr, nemc->base + NEMC_NFCSR); | ||
109 | } | ||
110 | EXPORT_SYMBOL(jz4780_nemc_set_type); | ||
111 | |||
112 | /** | ||
113 | * jz4780_nemc_assert() - (de-)assert a NAND device's chip enable pin | ||
114 | * @dev: child device of the NEMC. | ||
115 | * @bank: bank number of device. | ||
116 | * @assert: whether the chip enable pin should be asserted. | ||
117 | * | ||
118 | * (De-)asserts the chip enable pin for the NAND device connected to the | ||
119 | * specified bank. | ||
120 | */ | ||
121 | void jz4780_nemc_assert(struct device *dev, unsigned int bank, bool assert) | ||
122 | { | ||
123 | struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent); | ||
124 | uint32_t nfcsr; | ||
125 | |||
126 | nfcsr = readl(nemc->base + NEMC_NFCSR); | ||
127 | |||
128 | if (assert) | ||
129 | nfcsr |= NEMC_NFCSR_NFCEn(bank); | ||
130 | else | ||
131 | nfcsr &= ~NEMC_NFCSR_NFCEn(bank); | ||
132 | |||
133 | writel(nfcsr, nemc->base + NEMC_NFCSR); | ||
134 | } | ||
135 | EXPORT_SYMBOL(jz4780_nemc_assert); | ||
136 | |||
137 | static uint32_t jz4780_nemc_clk_period(struct jz4780_nemc *nemc) | ||
138 | { | ||
139 | unsigned long rate; | ||
140 | |||
141 | rate = clk_get_rate(nemc->clk); | ||
142 | if (!rate) | ||
143 | return 0; | ||
144 | |||
145 | /* Return in picoseconds. */ | ||
146 | return div64_ul(1000000000000ull, rate); | ||
147 | } | ||
148 | |||
149 | static uint32_t jz4780_nemc_ns_to_cycles(struct jz4780_nemc *nemc, uint32_t ns) | ||
150 | { | ||
151 | return ((ns * 1000) + nemc->clk_period - 1) / nemc->clk_period; | ||
152 | } | ||
153 | |||
154 | static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc, | ||
155 | unsigned int bank, | ||
156 | struct device_node *node) | ||
157 | { | ||
158 | uint32_t smcr, val, cycles; | ||
159 | |||
160 | /* | ||
161 | * Conversion of tBP and tAW cycle counts to values supported by the | ||
162 | * hardware (round up to the next supported value). | ||
163 | */ | ||
164 | static const uint32_t convert_tBP_tAW[] = { | ||
165 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, | ||
166 | |||
167 | /* 11 - 12 -> 12 cycles */ | ||
168 | 11, 11, | ||
169 | |||
170 | /* 13 - 15 -> 15 cycles */ | ||
171 | 12, 12, 12, | ||
172 | |||
173 | /* 16 - 20 -> 20 cycles */ | ||
174 | 13, 13, 13, 13, 13, | ||
175 | |||
176 | /* 21 - 25 -> 25 cycles */ | ||
177 | 14, 14, 14, 14, 14, | ||
178 | |||
179 | /* 26 - 31 -> 31 cycles */ | ||
180 | 15, 15, 15, 15, 15, 15 | ||
181 | }; | ||
182 | |||
183 | smcr = readl(nemc->base + NEMC_SMCRn(bank)); | ||
184 | smcr &= ~NEMC_SMCR_SMT; | ||
185 | |||
186 | if (!of_property_read_u32(node, "ingenic,nemc-bus-width", &val)) { | ||
187 | smcr &= ~NEMC_SMCR_BW_MASK; | ||
188 | switch (val) { | ||
189 | case 8: | ||
190 | smcr |= NEMC_SMCR_BW_8; | ||
191 | break; | ||
192 | default: | ||
193 | /* | ||
194 | * Earlier SoCs support a 16 bit bus width (the 4780 | ||
195 | * does not), until those are properly supported, error. | ||
196 | */ | ||
197 | dev_err(nemc->dev, "unsupported bus width: %u\n", val); | ||
198 | return false; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) { | ||
203 | smcr &= ~NEMC_SMCR_TAS_MASK; | ||
204 | cycles = jz4780_nemc_ns_to_cycles(nemc, val); | ||
205 | if (cycles > 15) { | ||
206 | dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n", | ||
207 | val, cycles); | ||
208 | return false; | ||
209 | } | ||
210 | |||
211 | smcr |= cycles << NEMC_SMCR_TAS_SHIFT; | ||
212 | } | ||
213 | |||
214 | if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) { | ||
215 | smcr &= ~NEMC_SMCR_TAH_MASK; | ||
216 | cycles = jz4780_nemc_ns_to_cycles(nemc, val); | ||
217 | if (cycles > 15) { | ||
218 | dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n", | ||
219 | val, cycles); | ||
220 | return false; | ||
221 | } | ||
222 | |||
223 | smcr |= cycles << NEMC_SMCR_TAH_SHIFT; | ||
224 | } | ||
225 | |||
226 | if (of_property_read_u32(node, "ingenic,nemc-tBP", &val) == 0) { | ||
227 | smcr &= ~NEMC_SMCR_TBP_MASK; | ||
228 | cycles = jz4780_nemc_ns_to_cycles(nemc, val); | ||
229 | if (cycles > 31) { | ||
230 | dev_err(nemc->dev, "tBP %u is too high (%u cycles)\n", | ||
231 | val, cycles); | ||
232 | return false; | ||
233 | } | ||
234 | |||
235 | smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TBP_SHIFT; | ||
236 | } | ||
237 | |||
238 | if (of_property_read_u32(node, "ingenic,nemc-tAW", &val) == 0) { | ||
239 | smcr &= ~NEMC_SMCR_TAW_MASK; | ||
240 | cycles = jz4780_nemc_ns_to_cycles(nemc, val); | ||
241 | if (cycles > 31) { | ||
242 | dev_err(nemc->dev, "tAW %u is too high (%u cycles)\n", | ||
243 | val, cycles); | ||
244 | return false; | ||
245 | } | ||
246 | |||
247 | smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TAW_SHIFT; | ||
248 | } | ||
249 | |||
250 | if (of_property_read_u32(node, "ingenic,nemc-tSTRV", &val) == 0) { | ||
251 | smcr &= ~NEMC_SMCR_TSTRV_MASK; | ||
252 | cycles = jz4780_nemc_ns_to_cycles(nemc, val); | ||
253 | if (cycles > 63) { | ||
254 | dev_err(nemc->dev, "tSTRV %u is too high (%u cycles)\n", | ||
255 | val, cycles); | ||
256 | return false; | ||
257 | } | ||
258 | |||
259 | smcr |= cycles << NEMC_SMCR_TSTRV_SHIFT; | ||
260 | } | ||
261 | |||
262 | writel(smcr, nemc->base + NEMC_SMCRn(bank)); | ||
263 | return true; | ||
264 | } | ||
265 | |||
266 | static int jz4780_nemc_probe(struct platform_device *pdev) | ||
267 | { | ||
268 | struct device *dev = &pdev->dev; | ||
269 | struct jz4780_nemc *nemc; | ||
270 | struct resource *res; | ||
271 | struct device_node *child; | ||
272 | const __be32 *prop; | ||
273 | unsigned int bank; | ||
274 | unsigned long referenced; | ||
275 | int i, ret; | ||
276 | |||
277 | nemc = devm_kzalloc(dev, sizeof(*nemc), GFP_KERNEL); | ||
278 | if (!nemc) | ||
279 | return -ENOMEM; | ||
280 | |||
281 | spin_lock_init(&nemc->lock); | ||
282 | nemc->dev = dev; | ||
283 | |||
284 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
285 | nemc->base = devm_ioremap_resource(dev, res); | ||
286 | if (IS_ERR(nemc->base)) { | ||
287 | dev_err(dev, "failed to get I/O memory\n"); | ||
288 | return PTR_ERR(nemc->base); | ||
289 | } | ||
290 | |||
291 | writel(0, nemc->base + NEMC_NFCSR); | ||
292 | |||
293 | nemc->clk = devm_clk_get(dev, NULL); | ||
294 | if (IS_ERR(nemc->clk)) { | ||
295 | dev_err(dev, "failed to get clock\n"); | ||
296 | return PTR_ERR(nemc->clk); | ||
297 | } | ||
298 | |||
299 | ret = clk_prepare_enable(nemc->clk); | ||
300 | if (ret) { | ||
301 | dev_err(dev, "failed to enable clock: %d\n", ret); | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | nemc->clk_period = jz4780_nemc_clk_period(nemc); | ||
306 | if (!nemc->clk_period) { | ||
307 | dev_err(dev, "failed to calculate clock period\n"); | ||
308 | clk_disable_unprepare(nemc->clk); | ||
309 | return -EINVAL; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Iterate over child devices, check that they do not conflict with | ||
314 | * each other, and register child devices for them. If a child device | ||
315 | * has invalid properties, it is ignored and no platform device is | ||
316 | * registered for it. | ||
317 | */ | ||
318 | for_each_child_of_node(nemc->dev->of_node, child) { | ||
319 | referenced = 0; | ||
320 | i = 0; | ||
321 | while ((prop = of_get_address(child, i++, NULL, NULL))) { | ||
322 | bank = of_read_number(prop, 1); | ||
323 | if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) { | ||
324 | dev_err(nemc->dev, | ||
325 | "%s requests invalid bank %u\n", | ||
326 | child->full_name, bank); | ||
327 | |||
328 | /* Will continue the outer loop below. */ | ||
329 | referenced = 0; | ||
330 | break; | ||
331 | } | ||
332 | |||
333 | referenced |= BIT(bank); | ||
334 | } | ||
335 | |||
336 | if (!referenced) { | ||
337 | dev_err(nemc->dev, "%s has no addresses\n", | ||
338 | child->full_name); | ||
339 | continue; | ||
340 | } else if (nemc->banks_present & referenced) { | ||
341 | dev_err(nemc->dev, "%s conflicts with another node\n", | ||
342 | child->full_name); | ||
343 | continue; | ||
344 | } | ||
345 | |||
346 | /* Configure bank parameters. */ | ||
347 | for_each_set_bit(bank, &referenced, JZ4780_NEMC_NUM_BANKS) { | ||
348 | if (!jz4780_nemc_configure_bank(nemc, bank, child)) { | ||
349 | referenced = 0; | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | if (referenced) { | ||
355 | if (of_platform_device_create(child, NULL, nemc->dev)) | ||
356 | nemc->banks_present |= referenced; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | platform_set_drvdata(pdev, nemc); | ||
361 | dev_info(dev, "JZ4780 NEMC initialised\n"); | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int jz4780_nemc_remove(struct platform_device *pdev) | ||
366 | { | ||
367 | struct jz4780_nemc *nemc = platform_get_drvdata(pdev); | ||
368 | |||
369 | clk_disable_unprepare(nemc->clk); | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static const struct of_device_id jz4780_nemc_dt_match[] = { | ||
374 | { .compatible = "ingenic,jz4780-nemc" }, | ||
375 | {}, | ||
376 | }; | ||
377 | |||
378 | static struct platform_driver jz4780_nemc_driver = { | ||
379 | .probe = jz4780_nemc_probe, | ||
380 | .remove = jz4780_nemc_remove, | ||
381 | .driver = { | ||
382 | .name = "jz4780-nemc", | ||
383 | .of_match_table = of_match_ptr(jz4780_nemc_dt_match), | ||
384 | }, | ||
385 | }; | ||
386 | |||
387 | static int __init jz4780_nemc_init(void) | ||
388 | { | ||
389 | return platform_driver_register(&jz4780_nemc_driver); | ||
390 | } | ||
391 | subsys_initcall(jz4780_nemc_init); | ||