]>
Commit | Line | Data |
---|---|---|
7ec94453 A |
1 | /* |
2 | * EMIF driver | |
3 | * | |
4 | * Copyright (C) 2012 Texas Instruments, Inc. | |
5 | * | |
6 | * Aneesh V <aneesh@ti.com> | |
7 | * Santosh Shilimkar <santosh.shilimkar@ti.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/reboot.h> | |
15 | #include <linux/platform_data/emif_plat.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/device.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/seq_file.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/list.h> | |
24 | #include <memory/jedec_ddr.h> | |
25 | #include "emif.h" | |
26 | ||
27 | /** | |
28 | * struct emif_data - Per device static data for driver's use | |
29 | * @duplicate: Whether the DDR devices attached to this EMIF | |
30 | * instance are exactly same as that on EMIF1. In | |
31 | * this case we can save some memory and processing | |
32 | * @temperature_level: Maximum temperature of LPDDR2 devices attached | |
33 | * to this EMIF - read from MR4 register. If there | |
34 | * are two devices attached to this EMIF, this | |
35 | * value is the maximum of the two temperature | |
36 | * levels. | |
37 | * @node: node in the device list | |
38 | * @base: base address of memory-mapped IO registers. | |
39 | * @dev: device pointer. | |
40 | * @plat_data: Pointer to saved platform data. | |
41 | */ | |
42 | struct emif_data { | |
43 | u8 duplicate; | |
44 | u8 temperature_level; | |
45 | struct list_head node; | |
46 | void __iomem *base; | |
47 | struct device *dev; | |
48 | struct emif_platform_data *plat_data; | |
49 | }; | |
50 | ||
51 | static struct emif_data *emif1; | |
52 | static LIST_HEAD(device_list); | |
53 | ||
54 | static void get_default_timings(struct emif_data *emif) | |
55 | { | |
56 | struct emif_platform_data *pd = emif->plat_data; | |
57 | ||
58 | pd->timings = lpddr2_jedec_timings; | |
59 | pd->timings_arr_size = ARRAY_SIZE(lpddr2_jedec_timings); | |
60 | ||
61 | dev_warn(emif->dev, "%s: using default timings\n", __func__); | |
62 | } | |
63 | ||
64 | static int is_dev_data_valid(u32 type, u32 density, u32 io_width, u32 phy_type, | |
65 | u32 ip_rev, struct device *dev) | |
66 | { | |
67 | int valid; | |
68 | ||
69 | valid = (type == DDR_TYPE_LPDDR2_S4 || | |
70 | type == DDR_TYPE_LPDDR2_S2) | |
71 | && (density >= DDR_DENSITY_64Mb | |
72 | && density <= DDR_DENSITY_8Gb) | |
73 | && (io_width >= DDR_IO_WIDTH_8 | |
74 | && io_width <= DDR_IO_WIDTH_32); | |
75 | ||
76 | /* Combinations of EMIF and PHY revisions that we support today */ | |
77 | switch (ip_rev) { | |
78 | case EMIF_4D: | |
79 | valid = valid && (phy_type == EMIF_PHY_TYPE_ATTILAPHY); | |
80 | break; | |
81 | case EMIF_4D5: | |
82 | valid = valid && (phy_type == EMIF_PHY_TYPE_INTELLIPHY); | |
83 | break; | |
84 | default: | |
85 | valid = 0; | |
86 | } | |
87 | ||
88 | if (!valid) | |
89 | dev_err(dev, "%s: invalid DDR details\n", __func__); | |
90 | return valid; | |
91 | } | |
92 | ||
93 | static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs, | |
94 | struct device *dev) | |
95 | { | |
96 | int valid = 1; | |
97 | ||
98 | if ((cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE) && | |
99 | (cust_cfgs->lpmode != EMIF_LP_MODE_DISABLE)) | |
100 | valid = cust_cfgs->lpmode_freq_threshold && | |
101 | cust_cfgs->lpmode_timeout_performance && | |
102 | cust_cfgs->lpmode_timeout_power; | |
103 | ||
104 | if (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL) | |
105 | valid = valid && cust_cfgs->temp_alert_poll_interval_ms; | |
106 | ||
107 | if (!valid) | |
108 | dev_warn(dev, "%s: invalid custom configs\n", __func__); | |
109 | ||
110 | return valid; | |
111 | } | |
112 | ||
113 | static struct emif_data *__init_or_module get_device_details( | |
114 | struct platform_device *pdev) | |
115 | { | |
116 | u32 size; | |
117 | struct emif_data *emif = NULL; | |
118 | struct ddr_device_info *dev_info; | |
119 | struct emif_custom_configs *cust_cfgs; | |
120 | struct emif_platform_data *pd; | |
121 | struct device *dev; | |
122 | void *temp; | |
123 | ||
124 | pd = pdev->dev.platform_data; | |
125 | dev = &pdev->dev; | |
126 | ||
127 | if (!(pd && pd->device_info && is_dev_data_valid(pd->device_info->type, | |
128 | pd->device_info->density, pd->device_info->io_width, | |
129 | pd->phy_type, pd->ip_rev, dev))) { | |
130 | dev_err(dev, "%s: invalid device data\n", __func__); | |
131 | goto error; | |
132 | } | |
133 | ||
134 | emif = devm_kzalloc(dev, sizeof(*emif), GFP_KERNEL); | |
135 | temp = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); | |
136 | dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL); | |
137 | ||
138 | if (!emif || !pd || !dev_info) { | |
139 | dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__); | |
140 | goto error; | |
141 | } | |
142 | ||
143 | memcpy(temp, pd, sizeof(*pd)); | |
144 | pd = temp; | |
145 | memcpy(dev_info, pd->device_info, sizeof(*dev_info)); | |
146 | ||
147 | pd->device_info = dev_info; | |
148 | emif->plat_data = pd; | |
149 | emif->dev = dev; | |
150 | emif->temperature_level = SDRAM_TEMP_NOMINAL; | |
151 | ||
152 | /* | |
153 | * For EMIF instances other than EMIF1 see if the devices connected | |
154 | * are exactly same as on EMIF1(which is typically the case). If so, | |
155 | * mark it as a duplicate of EMIF1 and skip copying timings data. | |
156 | * This will save some memory and some computation later. | |
157 | */ | |
158 | emif->duplicate = emif1 && (memcmp(dev_info, | |
159 | emif1->plat_data->device_info, | |
160 | sizeof(struct ddr_device_info)) == 0); | |
161 | ||
162 | if (emif->duplicate) { | |
163 | pd->timings = NULL; | |
164 | pd->min_tck = NULL; | |
165 | goto out; | |
166 | } else if (emif1) { | |
167 | dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n", | |
168 | __func__); | |
169 | } | |
170 | ||
171 | /* | |
172 | * Copy custom configs - ignore allocation error, if any, as | |
173 | * custom_configs is not very critical | |
174 | */ | |
175 | cust_cfgs = pd->custom_configs; | |
176 | if (cust_cfgs && is_custom_config_valid(cust_cfgs, dev)) { | |
177 | temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL); | |
178 | if (temp) | |
179 | memcpy(temp, cust_cfgs, sizeof(*cust_cfgs)); | |
180 | else | |
181 | dev_warn(dev, "%s:%d: allocation error\n", __func__, | |
182 | __LINE__); | |
183 | pd->custom_configs = temp; | |
184 | } | |
185 | ||
186 | /* | |
187 | * Copy timings and min-tck values from platform data. If it is not | |
188 | * available or if memory allocation fails, use JEDEC defaults | |
189 | */ | |
190 | size = sizeof(struct lpddr2_timings) * pd->timings_arr_size; | |
191 | if (pd->timings) { | |
192 | temp = devm_kzalloc(dev, size, GFP_KERNEL); | |
193 | if (temp) { | |
194 | memcpy(temp, pd->timings, sizeof(*pd->timings)); | |
195 | pd->timings = temp; | |
196 | } else { | |
197 | dev_warn(dev, "%s:%d: allocation error\n", __func__, | |
198 | __LINE__); | |
199 | get_default_timings(emif); | |
200 | } | |
201 | } else { | |
202 | get_default_timings(emif); | |
203 | } | |
204 | ||
205 | if (pd->min_tck) { | |
206 | temp = devm_kzalloc(dev, sizeof(*pd->min_tck), GFP_KERNEL); | |
207 | if (temp) { | |
208 | memcpy(temp, pd->min_tck, sizeof(*pd->min_tck)); | |
209 | pd->min_tck = temp; | |
210 | } else { | |
211 | dev_warn(dev, "%s:%d: allocation error\n", __func__, | |
212 | __LINE__); | |
213 | pd->min_tck = &lpddr2_jedec_min_tck; | |
214 | } | |
215 | } else { | |
216 | pd->min_tck = &lpddr2_jedec_min_tck; | |
217 | } | |
218 | ||
219 | out: | |
220 | return emif; | |
221 | ||
222 | error: | |
223 | return NULL; | |
224 | } | |
225 | ||
226 | static int __init_or_module emif_probe(struct platform_device *pdev) | |
227 | { | |
228 | struct emif_data *emif; | |
229 | struct resource *res; | |
230 | ||
231 | emif = get_device_details(pdev); | |
232 | if (!emif) { | |
233 | pr_err("%s: error getting device data\n", __func__); | |
234 | goto error; | |
235 | } | |
236 | ||
237 | if (!emif1) | |
238 | emif1 = emif; | |
239 | ||
240 | list_add(&emif->node, &device_list); | |
241 | ||
242 | /* Save pointers to each other in emif and device structures */ | |
243 | emif->dev = &pdev->dev; | |
244 | platform_set_drvdata(pdev, emif); | |
245 | ||
246 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
247 | if (!res) { | |
248 | dev_err(emif->dev, "%s: error getting memory resource\n", | |
249 | __func__); | |
250 | goto error; | |
251 | } | |
252 | ||
253 | emif->base = devm_request_and_ioremap(emif->dev, res); | |
254 | if (!emif->base) { | |
255 | dev_err(emif->dev, "%s: devm_request_and_ioremap() failed\n", | |
256 | __func__); | |
257 | goto error; | |
258 | } | |
259 | ||
260 | dev_info(&pdev->dev, "%s: device configured with addr = %p\n", | |
261 | __func__, emif->base); | |
262 | ||
263 | return 0; | |
264 | error: | |
265 | return -ENODEV; | |
266 | } | |
267 | ||
268 | static struct platform_driver emif_driver = { | |
269 | .driver = { | |
270 | .name = "emif", | |
271 | }, | |
272 | }; | |
273 | ||
274 | static int __init_or_module emif_register(void) | |
275 | { | |
276 | return platform_driver_probe(&emif_driver, emif_probe); | |
277 | } | |
278 | ||
279 | static void __exit emif_unregister(void) | |
280 | { | |
281 | platform_driver_unregister(&emif_driver); | |
282 | } | |
283 | ||
284 | module_init(emif_register); | |
285 | module_exit(emif_unregister); | |
286 | MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver"); | |
287 | MODULE_LICENSE("GPL"); | |
288 | MODULE_ALIAS("platform:emif"); | |
289 | MODULE_AUTHOR("Texas Instruments Inc"); |