aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/memory-devices/ti-emif.txt57
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-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
-rw-r--r--include/linux/platform_data/emif_plat.h128
8 files changed, 511 insertions, 0 deletions
diff --git a/Documentation/memory-devices/ti-emif.txt b/Documentation/memory-devices/ti-emif.txt
new file mode 100644
index 000000000000..f4ad9a7d0f4b
--- /dev/null
+++ b/Documentation/memory-devices/ti-emif.txt
@@ -0,0 +1,57 @@
1TI EMIF SDRAM Controller Driver:
2
3Author
4========
5Aneesh V <aneesh@ti.com>
6
7Location
8============
9driver/memory/emif.c
10
11Supported SoCs:
12===================
13TI OMAP44xx
14TI OMAP54xx
15
16Menuconfig option:
17==========================
18Device Drivers
19 Memory devices
20 Texas Instruments EMIF driver
21
22Description
23===========
24This driver is for the EMIF module available in Texas Instruments
25SoCs. EMIF is an SDRAM controller that, based on its revision,
26supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
27This driver takes care of only LPDDR2 memories presently. The
28functions of the driver includes re-configuring AC timing
29parameters and other settings during frequency, voltage and
30temperature changes
31
32Platform Data (see include/linux/platform_data/emif_plat.h):
33=====================================================================
34DDR device details and other board dependent and SoC dependent
35information can be passed through platform data (struct emif_platform_data)
36- DDR device details: 'struct ddr_device_info'
37- Device AC timings: 'struct lpddr2_timings' and 'struct lpddr2_min_tck'
38- Custom configurations: customizable policy options through
39 'struct emif_custom_configs'
40- IP revision
41- PHY type
42
43Interface to the external world:
44================================
45EMIF driver registers notifiers for voltage and frequency changes
46affecting EMIF and takes appropriate actions when these are invoked.
47- freq_pre_notify_handling()
48- freq_post_notify_handling()
49- volt_notify_handling()
50
51Debugfs
52========
53The driver creates two debugfs entries per device.
54- regcache_dump : dump of register values calculated and saved for all
55 frequencies used so far.
56- mr4 : last polled value of MR4 register in the LPDDR2 device. MR4
57 indicates the current temperature level of the device.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 0233ad979b7d..63b81826cb55 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -142,4 +142,6 @@ source "drivers/devfreq/Kconfig"
142 142
143source "drivers/extcon/Kconfig" 143source "drivers/extcon/Kconfig"
144 144
145source "drivers/memory/Kconfig"
146
145endmenu 147endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index c41dfa92cd79..265b506a15be 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -135,3 +135,4 @@ obj-$(CONFIG_HYPERV) += hv/
135 135
136obj-$(CONFIG_PM_DEVFREQ) += devfreq/ 136obj-$(CONFIG_PM_DEVFREQ) += devfreq/
137obj-$(CONFIG_EXTCON) += extcon/ 137obj-$(CONFIG_EXTCON) += extcon/
138obj-$(CONFIG_MEMORY) += memory/
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
diff --git a/include/linux/platform_data/emif_plat.h b/include/linux/platform_data/emif_plat.h
new file mode 100644
index 000000000000..03378ca84061
--- /dev/null
+++ b/include/linux/platform_data/emif_plat.h
@@ -0,0 +1,128 @@
1/*
2 * Definitions for TI EMIF device platform data
3 *
4 * Copyright (C) 2012 Texas Instruments, Inc.
5 *
6 * Aneesh V <aneesh@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#ifndef __EMIF_PLAT_H
13#define __EMIF_PLAT_H
14
15/* Low power modes - EMIF_PWR_MGMT_CTRL */
16#define EMIF_LP_MODE_DISABLE 0
17#define EMIF_LP_MODE_CLOCK_STOP 1
18#define EMIF_LP_MODE_SELF_REFRESH 2
19#define EMIF_LP_MODE_PWR_DN 4
20
21/* Hardware capabilities */
22#define EMIF_HW_CAPS_LL_INTERFACE 0x00000001
23
24/*
25 * EMIF IP Revisions
26 * EMIF4D - Used in OMAP4
27 * EMIF4D5 - Used in OMAP5
28 */
29#define EMIF_4D 1
30#define EMIF_4D5 2
31
32/*
33 * PHY types
34 * ATTILAPHY - Used in OMAP4
35 * INTELLIPHY - Used in OMAP5
36 */
37#define EMIF_PHY_TYPE_ATTILAPHY 1
38#define EMIF_PHY_TYPE_INTELLIPHY 2
39
40/* Custom config requests */
41#define EMIF_CUSTOM_CONFIG_LPMODE 0x00000001
42#define EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL 0x00000002
43
44#ifndef __ASSEMBLY__
45/**
46 * struct ddr_device_info - All information about the DDR device except AC
47 * timing parameters
48 * @type: Device type (LPDDR2-S4, LPDDR2-S2 etc)
49 * @density: Device density
50 * @io_width: Bus width
51 * @cs1_used: Whether there is a DDR device attached to the second
52 * chip-select(CS1) of this EMIF instance
53 * @cal_resistors_per_cs: Whether there is one calibration resistor per
54 * chip-select or whether it's a single one for both
55 * @manufacturer: Manufacturer name string
56 */
57struct ddr_device_info {
58 u32 type;
59 u32 density;
60 u32 io_width;
61 u32 cs1_used;
62 u32 cal_resistors_per_cs;
63 char manufacturer[10];
64};
65
66/**
67 * struct emif_custom_configs - Custom configuration parameters/policies
68 * passed from the platform layer
69 * @mask: Mask to indicate which configs are requested
70 * @lpmode: LPMODE to be used in PWR_MGMT_CTRL register
71 * @lpmode_timeout_performance: Timeout before LPMODE entry when higher
72 * performance is desired at the cost of power (typically
73 * at higher OPPs)
74 * @lpmode_timeout_power: Timeout before LPMODE entry when better power
75 * savings is desired and performance is not important
76 * (typically at lower loads indicated by lower OPPs)
77 * @lpmode_freq_threshold: The DDR frequency threshold to identify between
78 * the above two cases:
79 * timeout = (freq >= lpmode_freq_threshold) ?
80 * lpmode_timeout_performance :
81 * lpmode_timeout_power;
82 * @temp_alert_poll_interval_ms: LPDDR2 MR4 polling interval at nominal
83 * temperature(in milliseconds). When temperature is high
84 * polling is done 4 times as frequently.
85 */
86struct emif_custom_configs {
87 u32 mask;
88 u32 lpmode;
89 u32 lpmode_timeout_performance;
90 u32 lpmode_timeout_power;
91 u32 lpmode_freq_threshold;
92 u32 temp_alert_poll_interval_ms;
93};
94
95/**
96 * struct emif_platform_data - Platform data passed on EMIF platform
97 * device creation. Used by the driver.
98 * @hw_caps: Hw capabilities of the EMIF IP in the respective SoC
99 * @device_info: Device info structure containing information such
100 * as type, bus width, density etc
101 * @timings: Timings information from device datasheet passed
102 * as an array of 'struct lpddr2_timings'. Can be NULL
103 * if if default timings are ok
104 * @timings_arr_size: Size of the timings array. Depends on the number
105 * of different frequencies for which timings data
106 * is provided
107 * @min_tck: Minimum value of some timing parameters in terms
108 * of number of cycles. Can be NULL if default values
109 * are ok
110 * @custom_configs: Custom configurations requested by SoC or board
111 * code and the data for them. Can be NULL if default
112 * configurations done by the driver are ok. See
113 * documentation for 'struct emif_custom_configs' for
114 * more details
115 */
116struct emif_platform_data {
117 u32 hw_caps;
118 struct ddr_device_info *device_info;
119 const struct lpddr2_timings *timings;
120 u32 timings_arr_size;
121 const struct lpddr2_min_tck *min_tck;
122 struct emif_custom_configs *custom_configs;
123 u32 ip_rev;
124 u32 phy_type;
125};
126#endif /* __ASSEMBLY__ */
127
128#endif /* __LINUX_EMIF_H */