diff options
author | Aneesh V <aneesh@ti.com> | 2012-04-27 08:24:05 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-02 03:10:49 -0400 |
commit | 7ec944538dde3d7f490bd4d2619051789db5c3c3 (patch) | |
tree | 03bbd82691ce1d620a14d31e8bc3f231232e660d /drivers/memory | |
parent | 6c8b0906cf447adf2aeeed3d79eb5cec7f362d1f (diff) |
memory: emif: add basic infrastructure for EMIF driver
EMIF is an SDRAM controller used in various Texas Instruments
SoCs. EMIF supports, based on its revision, one or more of
LPDDR2/DDR2/DDR3 protocols.
Add the basic infrastructure for EMIF driver that includes
driver registration, probe, parsing of platform data etc.
Signed-off-by: Aneesh V <aneesh@ti.com>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Benoit Cousson <b-cousson@ti.com>
[santosh.shilimkar@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Tested-by: Lokesh Vutla <lokeshvutla@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/memory')
-rw-r--r-- | drivers/memory/Kconfig | 22 | ||||
-rw-r--r-- | drivers/memory/Makefile | 5 | ||||
-rw-r--r-- | drivers/memory/emif.c | 289 | ||||
-rw-r--r-- | drivers/memory/emif.h | 7 |
4 files changed, 323 insertions, 0 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig new file mode 100644 index 000000000000..b08327cca0e5 --- /dev/null +++ b/drivers/memory/Kconfig | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # Memory devices | ||
3 | # | ||
4 | |||
5 | menuconfig MEMORY | ||
6 | bool "Memory Controller drivers" | ||
7 | |||
8 | if MEMORY | ||
9 | |||
10 | config TI_EMIF | ||
11 | tristate "Texas Instruments EMIF driver" | ||
12 | select DDR | ||
13 | help | ||
14 | This driver is for the EMIF module available in Texas Instruments | ||
15 | SoCs. EMIF is an SDRAM controller that, based on its revision, | ||
16 | supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols. | ||
17 | This driver takes care of only LPDDR2 memories presently. The | ||
18 | functions of the driver includes re-configuring AC timing | ||
19 | parameters and other settings during frequency, voltage and | ||
20 | temperature changes | ||
21 | |||
22 | endif | ||
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile new file mode 100644 index 000000000000..e27f80b28859 --- /dev/null +++ b/drivers/memory/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for memory devices | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_TI_EMIF) += emif.o | ||
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c new file mode 100644 index 000000000000..7486d7ef0826 --- /dev/null +++ b/drivers/memory/emif.c | |||
@@ -0,0 +1,289 @@ | |||
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"); | ||
diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h index 44b97dfe95b4..692b2a864e7b 100644 --- a/drivers/memory/emif.h +++ b/drivers/memory/emif.h | |||
@@ -12,6 +12,13 @@ | |||
12 | #ifndef __EMIF_H | 12 | #ifndef __EMIF_H |
13 | #define __EMIF_H | 13 | #define __EMIF_H |
14 | 14 | ||
15 | /* | ||
16 | * Maximum number of different frequencies supported by EMIF driver | ||
17 | * Determines the number of entries in the pointer array for register | ||
18 | * cache | ||
19 | */ | ||
20 | #define EMIF_MAX_NUM_FREQUENCIES 6 | ||
21 | |||
15 | /* Registers offset */ | 22 | /* Registers offset */ |
16 | #define EMIF_MODULE_ID_AND_REVISION 0x0000 | 23 | #define EMIF_MODULE_ID_AND_REVISION 0x0000 |
17 | #define EMIF_STATUS 0x0004 | 24 | #define EMIF_STATUS 0x0004 |