aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/memory
diff options
context:
space:
mode:
authorAneesh V <aneesh@ti.com>2012-04-27 08:24:05 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-02 03:10:49 -0400
commit7ec944538dde3d7f490bd4d2619051789db5c3c3 (patch)
tree03bbd82691ce1d620a14d31e8bc3f231232e660d /drivers/memory
parent6c8b0906cf447adf2aeeed3d79eb5cec7f362d1f (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/Kconfig22
-rw-r--r--drivers/memory/Makefile5
-rw-r--r--drivers/memory/emif.c289
-rw-r--r--drivers/memory/emif.h7
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
5menuconfig MEMORY
6 bool "Memory Controller drivers"
7
8if MEMORY
9
10config 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
22endif
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
5obj-$(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 */
42struct 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
51static struct emif_data *emif1;
52static LIST_HEAD(device_list);
53
54static 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
64static 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
93static 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
113static 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
219out:
220 return emif;
221
222error:
223 return NULL;
224}
225
226static 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;
264error:
265 return -ENODEV;
266}
267
268static struct platform_driver emif_driver = {
269 .driver = {
270 .name = "emif",
271 },
272};
273
274static int __init_or_module emif_register(void)
275{
276 return platform_driver_probe(&emif_driver, emif_probe);
277}
278
279static void __exit emif_unregister(void)
280{
281 platform_driver_unregister(&emif_driver);
282}
283
284module_init(emif_register);
285module_exit(emif_unregister);
286MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
287MODULE_LICENSE("GPL");
288MODULE_ALIAS("platform:emif");
289MODULE_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