Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 304063
b: refs/heads/master
c: 7ec9445
h: refs/heads/master
i:
  304061: ddd79d8
  304059: c39ada4
  304055: 4c282ad
  304047: 4fc8530
  304031: 62ca5b3
  303999: fb42631
v: v3
  • Loading branch information
Aneesh V authored and Greg Kroah-Hartman committed May 2, 2012
1 parent f95fa4a commit 3802437
Show file tree
Hide file tree
Showing 9 changed files with 512 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6c8b0906cf447adf2aeeed3d79eb5cec7f362d1f
refs/heads/master: 7ec944538dde3d7f490bd4d2619051789db5c3c3
57 changes: 57 additions & 0 deletions trunk/Documentation/memory-devices/ti-emif.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
TI EMIF SDRAM Controller Driver:

Author
========
Aneesh V <aneesh@ti.com>

Location
============
driver/memory/emif.c

Supported SoCs:
===================
TI OMAP44xx
TI OMAP54xx

Menuconfig option:
==========================
Device Drivers
Memory devices
Texas Instruments EMIF driver

Description
===========
This driver is for the EMIF module available in Texas Instruments
SoCs. EMIF is an SDRAM controller that, based on its revision,
supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
This driver takes care of only LPDDR2 memories presently. The
functions of the driver includes re-configuring AC timing
parameters and other settings during frequency, voltage and
temperature changes

Platform Data (see include/linux/platform_data/emif_plat.h):
=====================================================================
DDR device details and other board dependent and SoC dependent
information can be passed through platform data (struct emif_platform_data)
- DDR device details: 'struct ddr_device_info'
- Device AC timings: 'struct lpddr2_timings' and 'struct lpddr2_min_tck'
- Custom configurations: customizable policy options through
'struct emif_custom_configs'
- IP revision
- PHY type

Interface to the external world:
================================
EMIF driver registers notifiers for voltage and frequency changes
affecting EMIF and takes appropriate actions when these are invoked.
- freq_pre_notify_handling()
- freq_post_notify_handling()
- volt_notify_handling()

Debugfs
========
The driver creates two debugfs entries per device.
- regcache_dump : dump of register values calculated and saved for all
frequencies used so far.
- mr4 : last polled value of MR4 register in the LPDDR2 device. MR4
indicates the current temperature level of the device.
2 changes: 2 additions & 0 deletions trunk/drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,6 @@ source "drivers/devfreq/Kconfig"

source "drivers/extcon/Kconfig"

source "drivers/memory/Kconfig"

endmenu
1 change: 1 addition & 0 deletions trunk/drivers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,4 @@ obj-$(CONFIG_HYPERV) += hv/

obj-$(CONFIG_PM_DEVFREQ) += devfreq/
obj-$(CONFIG_EXTCON) += extcon/
obj-$(CONFIG_MEMORY) += memory/
22 changes: 22 additions & 0 deletions trunk/drivers/memory/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
# Memory devices
#

menuconfig MEMORY
bool "Memory Controller drivers"

if MEMORY

config TI_EMIF
tristate "Texas Instruments EMIF driver"
select DDR
help
This driver is for the EMIF module available in Texas Instruments
SoCs. EMIF is an SDRAM controller that, based on its revision,
supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
This driver takes care of only LPDDR2 memories presently. The
functions of the driver includes re-configuring AC timing
parameters and other settings during frequency, voltage and
temperature changes

endif
5 changes: 5 additions & 0 deletions trunk/drivers/memory/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Makefile for memory devices
#

obj-$(CONFIG_TI_EMIF) += emif.o
289 changes: 289 additions & 0 deletions trunk/drivers/memory/emif.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
/*
* EMIF driver
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* Aneesh V <aneesh@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/platform_data/emif_plat.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/list.h>
#include <memory/jedec_ddr.h>
#include "emif.h"

/**
* struct emif_data - Per device static data for driver's use
* @duplicate: Whether the DDR devices attached to this EMIF
* instance are exactly same as that on EMIF1. In
* this case we can save some memory and processing
* @temperature_level: Maximum temperature of LPDDR2 devices attached
* to this EMIF - read from MR4 register. If there
* are two devices attached to this EMIF, this
* value is the maximum of the two temperature
* levels.
* @node: node in the device list
* @base: base address of memory-mapped IO registers.
* @dev: device pointer.
* @plat_data: Pointer to saved platform data.
*/
struct emif_data {
u8 duplicate;
u8 temperature_level;
struct list_head node;
void __iomem *base;
struct device *dev;
struct emif_platform_data *plat_data;
};

static struct emif_data *emif1;
static LIST_HEAD(device_list);

static void get_default_timings(struct emif_data *emif)
{
struct emif_platform_data *pd = emif->plat_data;

pd->timings = lpddr2_jedec_timings;
pd->timings_arr_size = ARRAY_SIZE(lpddr2_jedec_timings);

dev_warn(emif->dev, "%s: using default timings\n", __func__);
}

static int is_dev_data_valid(u32 type, u32 density, u32 io_width, u32 phy_type,
u32 ip_rev, struct device *dev)
{
int valid;

valid = (type == DDR_TYPE_LPDDR2_S4 ||
type == DDR_TYPE_LPDDR2_S2)
&& (density >= DDR_DENSITY_64Mb
&& density <= DDR_DENSITY_8Gb)
&& (io_width >= DDR_IO_WIDTH_8
&& io_width <= DDR_IO_WIDTH_32);

/* Combinations of EMIF and PHY revisions that we support today */
switch (ip_rev) {
case EMIF_4D:
valid = valid && (phy_type == EMIF_PHY_TYPE_ATTILAPHY);
break;
case EMIF_4D5:
valid = valid && (phy_type == EMIF_PHY_TYPE_INTELLIPHY);
break;
default:
valid = 0;
}

if (!valid)
dev_err(dev, "%s: invalid DDR details\n", __func__);
return valid;
}

static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
struct device *dev)
{
int valid = 1;

if ((cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE) &&
(cust_cfgs->lpmode != EMIF_LP_MODE_DISABLE))
valid = cust_cfgs->lpmode_freq_threshold &&
cust_cfgs->lpmode_timeout_performance &&
cust_cfgs->lpmode_timeout_power;

if (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL)
valid = valid && cust_cfgs->temp_alert_poll_interval_ms;

if (!valid)
dev_warn(dev, "%s: invalid custom configs\n", __func__);

return valid;
}

static struct emif_data *__init_or_module get_device_details(
struct platform_device *pdev)
{
u32 size;
struct emif_data *emif = NULL;
struct ddr_device_info *dev_info;
struct emif_custom_configs *cust_cfgs;
struct emif_platform_data *pd;
struct device *dev;
void *temp;

pd = pdev->dev.platform_data;
dev = &pdev->dev;

if (!(pd && pd->device_info && is_dev_data_valid(pd->device_info->type,
pd->device_info->density, pd->device_info->io_width,
pd->phy_type, pd->ip_rev, dev))) {
dev_err(dev, "%s: invalid device data\n", __func__);
goto error;
}

emif = devm_kzalloc(dev, sizeof(*emif), GFP_KERNEL);
temp = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);

if (!emif || !pd || !dev_info) {
dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__);
goto error;
}

memcpy(temp, pd, sizeof(*pd));
pd = temp;
memcpy(dev_info, pd->device_info, sizeof(*dev_info));

pd->device_info = dev_info;
emif->plat_data = pd;
emif->dev = dev;
emif->temperature_level = SDRAM_TEMP_NOMINAL;

/*
* For EMIF instances other than EMIF1 see if the devices connected
* are exactly same as on EMIF1(which is typically the case). If so,
* mark it as a duplicate of EMIF1 and skip copying timings data.
* This will save some memory and some computation later.
*/
emif->duplicate = emif1 && (memcmp(dev_info,
emif1->plat_data->device_info,
sizeof(struct ddr_device_info)) == 0);

if (emif->duplicate) {
pd->timings = NULL;
pd->min_tck = NULL;
goto out;
} else if (emif1) {
dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n",
__func__);
}

/*
* Copy custom configs - ignore allocation error, if any, as
* custom_configs is not very critical
*/
cust_cfgs = pd->custom_configs;
if (cust_cfgs && is_custom_config_valid(cust_cfgs, dev)) {
temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL);
if (temp)
memcpy(temp, cust_cfgs, sizeof(*cust_cfgs));
else
dev_warn(dev, "%s:%d: allocation error\n", __func__,
__LINE__);
pd->custom_configs = temp;
}

/*
* Copy timings and min-tck values from platform data. If it is not
* available or if memory allocation fails, use JEDEC defaults
*/
size = sizeof(struct lpddr2_timings) * pd->timings_arr_size;
if (pd->timings) {
temp = devm_kzalloc(dev, size, GFP_KERNEL);
if (temp) {
memcpy(temp, pd->timings, sizeof(*pd->timings));
pd->timings = temp;
} else {
dev_warn(dev, "%s:%d: allocation error\n", __func__,
__LINE__);
get_default_timings(emif);
}
} else {
get_default_timings(emif);
}

if (pd->min_tck) {
temp = devm_kzalloc(dev, sizeof(*pd->min_tck), GFP_KERNEL);
if (temp) {
memcpy(temp, pd->min_tck, sizeof(*pd->min_tck));
pd->min_tck = temp;
} else {
dev_warn(dev, "%s:%d: allocation error\n", __func__,
__LINE__);
pd->min_tck = &lpddr2_jedec_min_tck;
}
} else {
pd->min_tck = &lpddr2_jedec_min_tck;
}

out:
return emif;

error:
return NULL;
}

static int __init_or_module emif_probe(struct platform_device *pdev)
{
struct emif_data *emif;
struct resource *res;

emif = get_device_details(pdev);
if (!emif) {
pr_err("%s: error getting device data\n", __func__);
goto error;
}

if (!emif1)
emif1 = emif;

list_add(&emif->node, &device_list);

/* Save pointers to each other in emif and device structures */
emif->dev = &pdev->dev;
platform_set_drvdata(pdev, emif);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(emif->dev, "%s: error getting memory resource\n",
__func__);
goto error;
}

emif->base = devm_request_and_ioremap(emif->dev, res);
if (!emif->base) {
dev_err(emif->dev, "%s: devm_request_and_ioremap() failed\n",
__func__);
goto error;
}

dev_info(&pdev->dev, "%s: device configured with addr = %p\n",
__func__, emif->base);

return 0;
error:
return -ENODEV;
}

static struct platform_driver emif_driver = {
.driver = {
.name = "emif",
},
};

static int __init_or_module emif_register(void)
{
return platform_driver_probe(&emif_driver, emif_probe);
}

static void __exit emif_unregister(void)
{
platform_driver_unregister(&emif_driver);
}

module_init(emif_register);
module_exit(emif_unregister);
MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:emif");
MODULE_AUTHOR("Texas Instruments Inc");
Loading

0 comments on commit 3802437

Please sign in to comment.