Commit b6a03d04 authored by Olof Johansson's avatar Olof Johansson

Merge tag 'omap-for-v3.9/gpmc-signed' of...

Merge tag 'omap-for-v3.9/gpmc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/drivers

From Tony Lindgren:
OMAP GPMC (General Purpose Memory Controller) changes to add
device tree bindings.

* tag 'omap-for-v3.9/gpmc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
  ARM: OMAP2+: gpmc: Add device tree documentation for elm handle
  ARM: OMAP2+: gpmc: add DT bindings for OneNAND
  ARM: OMAP2+: gpmc-onenand: drop __init annotation
  mtd: omap-onenand: pass device_node in platform data
  ARM: OMAP2+: Prevent potential crash if GPMC probe fails
  ARM: OMAP2+: gpmc: Remove unneeded of_node_put()
  ARM: OMAP: gpmc: add DT bindings for GPMC timings and NAND
  ARM: OMAP: gpmc: enable hwecc for AM33xx SoCs
  ARM: OMAP: gpmc-nand: drop __init annotation
  mtd: omap-nand: pass device_node in platform data
  ARM: OMAP: gpmc: don't create devices from initcall on DT
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 3e93093e 97c794a1
Device tree bindings for OMAP general purpose memory controllers (GPMC)
The actual devices are instantiated from the child nodes of a GPMC node.
Required properties:
- compatible: Should be set to one of the following:
ti,omap2420-gpmc (omap2420)
ti,omap2430-gpmc (omap2430)
ti,omap3430-gpmc (omap3430 & omap3630)
ti,omap4430-gpmc (omap4430 & omap4460 & omap543x)
ti,am3352-gpmc (am335x devices)
- reg: A resource specifier for the register space
(see the example below)
- ti,hwmods: Should be set to "ti,gpmc" until the DT transition is
completed.
- #address-cells: Must be set to 2 to allow memory address translation
- #size-cells: Must be set to 1 to allow CS address passing
- gpmc,num-cs: The maximum number of chip-select lines that controller
can support.
- gpmc,num-waitpins: The maximum number of wait pins that controller can
support.
- ranges: Must be set up to reflect the memory layout with four
integer values for each chip-select line in use:
<cs-number> 0 <physical address of mapping> <size>
Currently, calculated values derived from the contents
of the per-CS register GPMC_CONFIG7 (as set up by the
bootloader) are used for the physical address decoding.
As this will change in the future, filling correct
values here is a requirement.
Timing properties for child nodes. All are optional and default to 0.
- gpmc,sync-clk: Minimum clock period for synchronous mode, in picoseconds
Chip-select signal timings corresponding to GPMC_CONFIG2:
- gpmc,cs-on: Assertion time
- gpmc,cs-rd-off: Read deassertion time
- gpmc,cs-wr-off: Write deassertion time
ADV signal timings corresponding to GPMC_CONFIG3:
- gpmc,adv-on: Assertion time
- gpmc,adv-rd-off: Read deassertion time
- gpmc,adv-wr-off: Write deassertion time
WE signals timings corresponding to GPMC_CONFIG4:
- gpmc,we-on: Assertion time
- gpmc,we-off: Deassertion time
OE signals timings corresponding to GPMC_CONFIG4:
- gpmc,oe-on: Assertion time
- gpmc,oe-off: Deassertion time
Access time and cycle time timings corresponding to GPMC_CONFIG5:
- gpmc,page-burst-access: Multiple access word delay
- gpmc,access: Start-cycle to first data valid delay
- gpmc,rd-cycle: Total read cycle time
- gpmc,wr-cycle: Total write cycle time
The following are only applicable to OMAP3+ and AM335x:
- gpmc,wr-access
- gpmc,wr-data-mux-bus
Example for an AM33xx board:
gpmc: gpmc@50000000 {
compatible = "ti,am3352-gpmc";
ti,hwmods = "gpmc";
reg = <0x50000000 0x2000>;
interrupts = <100>;
gpmc,num-cs = <8>;
gpmc,num-waitpins = <2>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
/* child nodes go here */
};
Device tree bindings for GPMC connected NANDs
GPMC connected NAND (found on OMAP boards) are represented as child nodes of
the GPMC controller with a name of "nand".
All timing relevant properties as well as generic gpmc child properties are
explained in a separate documents - please refer to
Documentation/devicetree/bindings/bus/ti-gpmc.txt
For NAND specific properties such as ECC modes or bus width, please refer to
Documentation/devicetree/bindings/mtd/nand.txt
Required properties:
- reg: The CS line the peripheral is connected to
Optional properties:
- nand-bus-width: Set this numeric value to 16 if the hardware
is wired that way. If not specified, a bus
width of 8 is assumed.
- ti,nand-ecc-opt: A string setting the ECC layout to use. One of:
"sw" Software method (default)
"hw" Hardware method
"hw-romcode" gpmc hamming mode method & romcode layout
"bch4" 4-bit BCH ecc code
"bch8" 8-bit BCH ecc code
- elm_id: Specifies elm device node. This is required to support BCH
error correction using ELM module.
For inline partiton table parsing (optional):
- #address-cells: should be set to 1
- #size-cells: should be set to 1
Example for an AM33xx board:
gpmc: gpmc@50000000 {
compatible = "ti,am3352-gpmc";
ti,hwmods = "gpmc";
reg = <0x50000000 0x1000000>;
interrupts = <100>;
gpmc,num-cs = <8>;
gpmc,num-waitpins = <2>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */
elm_id = <&elm>;
nand@0,0 {
reg = <0 0 0>; /* CS0, offset 0 */
nand-bus-width = <16>;
ti,nand-ecc-opt = "bch8";
gpmc,sync-clk = <0>;
gpmc,cs-on = <0>;
gpmc,cs-rd-off = <44>;
gpmc,cs-wr-off = <44>;
gpmc,adv-on = <6>;
gpmc,adv-rd-off = <34>;
gpmc,adv-wr-off = <44>;
gpmc,we-off = <40>;
gpmc,oe-off = <54>;
gpmc,access = <64>;
gpmc,rd-cycle = <82>;
gpmc,wr-cycle = <82>;
gpmc,wr-access = <40>;
gpmc,wr-data-mux-bus = <0>;
#address-cells = <1>;
#size-cells = <1>;
/* partitions go here */
};
};
Device tree bindings for GPMC connected OneNANDs
GPMC connected OneNAND (found on OMAP boards) are represented as child nodes of
the GPMC controller with a name of "onenand".
All timing relevant properties as well as generic gpmc child properties are
explained in a separate documents - please refer to
Documentation/devicetree/bindings/bus/ti-gpmc.txt
Required properties:
- reg: The CS line the peripheral is connected to
Optional properties:
- dma-channel: DMA Channel index
For inline partiton table parsing (optional):
- #address-cells: should be set to 1
- #size-cells: should be set to 1
Example for an OMAP3430 board:
gpmc: gpmc@6e000000 {
compatible = "ti,omap3430-gpmc";
ti,hwmods = "gpmc";
reg = <0x6e000000 0x1000000>;
interrupts = <20>;
gpmc,num-cs = <8>;
gpmc,num-waitpins = <4>;
#address-cells = <2>;
#size-cells = <1>;
onenand@0 {
reg = <0 0 0>; /* CS0, offset 0 */
#address-cells = <1>;
#size-cells = <1>;
/* partitions go here */
};
};
...@@ -89,20 +89,21 @@ static int omap2_nand_gpmc_retime( ...@@ -89,20 +89,21 @@ static int omap2_nand_gpmc_retime(
return 0; return 0;
} }
static bool __init gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt) static bool gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)
{ {
/* support only OMAP3 class */ /* support only OMAP3 class */
if (!cpu_is_omap34xx()) { if (!cpu_is_omap34xx() && !soc_is_am33xx()) {
pr_err("BCH ecc is not supported on this CPU\n"); pr_err("BCH ecc is not supported on this CPU\n");
return 0; return 0;
} }
/* /*
* For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1. * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1
* Other chips may be added if confirmed to work. * and AM33xx derivates. Other chips may be added if confirmed to work.
*/ */
if ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) && if ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) &&
(!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) { (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0)) &&
(!soc_is_am33xx())) {
pr_err("BCH 4-bit mode is not supported on this CPU\n"); pr_err("BCH 4-bit mode is not supported on this CPU\n");
return 0; return 0;
} }
...@@ -110,8 +111,8 @@ static bool __init gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt) ...@@ -110,8 +111,8 @@ static bool __init gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)
return 1; return 1;
} }
int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
struct gpmc_timings *gpmc_t) struct gpmc_timings *gpmc_t)
{ {
int err = 0; int err = 0;
struct device *dev = &gpmc_nand_device.dev; struct device *dev = &gpmc_nand_device.dev;
......
...@@ -356,7 +356,7 @@ static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr) ...@@ -356,7 +356,7 @@ static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr)
return ret; return ret;
} }
void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
{ {
int err; int err;
......
...@@ -25,6 +25,10 @@ ...@@ -25,6 +25,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/of_device.h>
#include <linux/mtd/nand.h>
#include <linux/platform_data/mtd-nand-omap2.h> #include <linux/platform_data/mtd-nand-omap2.h>
...@@ -34,6 +38,8 @@ ...@@ -34,6 +38,8 @@
#include "common.h" #include "common.h"
#include "omap_device.h" #include "omap_device.h"
#include "gpmc.h" #include "gpmc.h"
#include "gpmc-nand.h"
#include "gpmc-onenand.h"
#define DEVICE_NAME "omap-gpmc" #define DEVICE_NAME "omap-gpmc"
...@@ -145,7 +151,8 @@ static unsigned gpmc_irq_start; ...@@ -145,7 +151,8 @@ static unsigned gpmc_irq_start;
static struct resource gpmc_mem_root; static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM]; static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock); static DEFINE_SPINLOCK(gpmc_mem_lock);
static unsigned int gpmc_cs_map; /* flag for cs which are initialized */ /* Define chip-selects as reserved by default until probe completes */
static unsigned int gpmc_cs_map = ((1 << GPMC_CS_NUM) - 1);
static struct device *gpmc_dev; static struct device *gpmc_dev;
static int gpmc_irq; static int gpmc_irq;
static resource_size_t phys_base, mem_size; static resource_size_t phys_base, mem_size;
...@@ -1118,9 +1125,216 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t, ...@@ -1118,9 +1125,216 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t,
/* TODO: remove, see function definition */ /* TODO: remove, see function definition */
gpmc_convert_ps_to_ns(gpmc_t); gpmc_convert_ps_to_ns(gpmc_t);
/* Now the GPMC is initialised, unreserve the chip-selects */
gpmc_cs_map = 0;
return 0; return 0;
} }
#ifdef CONFIG_OF
static struct of_device_id gpmc_dt_ids[] = {
{ .compatible = "ti,omap2420-gpmc" },
{ .compatible = "ti,omap2430-gpmc" },
{ .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */
{ .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
{ }
};
MODULE_DEVICE_TABLE(of, gpmc_dt_ids);
static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
struct gpmc_timings *gpmc_t)
{
u32 val;
memset(gpmc_t, 0, sizeof(*gpmc_t));
/* minimum clock period for syncronous mode */
if (!of_property_read_u32(np, "gpmc,sync-clk", &val))
gpmc_t->sync_clk = val;
/* chip select timtings */
if (!of_property_read_u32(np, "gpmc,cs-on", &val))
gpmc_t->cs_on = val;
if (!of_property_read_u32(np, "gpmc,cs-rd-off", &val))
gpmc_t->cs_rd_off = val;
if (!of_property_read_u32(np, "gpmc,cs-wr-off", &val))
gpmc_t->cs_wr_off = val;
/* ADV signal timings */
if (!of_property_read_u32(np, "gpmc,adv-on", &val))
gpmc_t->adv_on = val;
if (!of_property_read_u32(np, "gpmc,adv-rd-off", &val))
gpmc_t->adv_rd_off = val;
if (!of_property_read_u32(np, "gpmc,adv-wr-off", &val))
gpmc_t->adv_wr_off = val;
/* WE signal timings */
if (!of_property_read_u32(np, "gpmc,we-on", &val))
gpmc_t->we_on = val;
if (!of_property_read_u32(np, "gpmc,we-off", &val))
gpmc_t->we_off = val;
/* OE signal timings */
if (!of_property_read_u32(np, "gpmc,oe-on", &val))
gpmc_t->oe_on = val;
if (!of_property_read_u32(np, "gpmc,oe-off", &val))
gpmc_t->oe_off = val;
/* access and cycle timings */
if (!of_property_read_u32(np, "gpmc,page-burst-access", &val))
gpmc_t->page_burst_access = val;
if (!of_property_read_u32(np, "gpmc,access", &val))
gpmc_t->access = val;
if (!of_property_read_u32(np, "gpmc,rd-cycle", &val))
gpmc_t->rd_cycle = val;
if (!of_property_read_u32(np, "gpmc,wr-cycle", &val))
gpmc_t->wr_cycle = val;
/* only for OMAP3430 */
if (!of_property_read_u32(np, "gpmc,wr-access", &val))
gpmc_t->wr_access = val;
if (!of_property_read_u32(np, "gpmc,wr-data-mux-bus", &val))
gpmc_t->wr_data_mux_bus = val;
}
#ifdef CONFIG_MTD_NAND
static const char * const nand_ecc_opts[] = {
[OMAP_ECC_HAMMING_CODE_DEFAULT] = "sw",
[OMAP_ECC_HAMMING_CODE_HW] = "hw",
[OMAP_ECC_HAMMING_CODE_HW_ROMCODE] = "hw-romcode",
[OMAP_ECC_BCH4_CODE_HW] = "bch4",
[OMAP_ECC_BCH8_CODE_HW] = "bch8",
};
static int gpmc_probe_nand_child(struct platform_device *pdev,
struct device_node *child)
{
u32 val;
const char *s;
struct gpmc_timings gpmc_t;
struct omap_nand_platform_data *gpmc_nand_data;
if (of_property_read_u32(child, "reg", &val) < 0) {
dev_err(&pdev->dev, "%s has no 'reg' property\n",
child->full_name);
return -ENODEV;
}
gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data),
GFP_KERNEL);
if (!gpmc_nand_data)
return -ENOMEM;
gpmc_nand_data->cs = val;
gpmc_nand_data->of_node = child;
if (!of_property_read_string(child, "ti,nand-ecc-opt", &s))
for (val = 0; val < ARRAY_SIZE(nand_ecc_opts); val++)
if (!strcasecmp(s, nand_ecc_opts[val])) {
gpmc_nand_data->ecc_opt = val;
break;
}
val = of_get_nand_bus_width(child);
if (val == 16)
gpmc_nand_data->devsize = NAND_BUSWIDTH_16;
gpmc_read_timings_dt(child, &gpmc_t);
gpmc_nand_init(gpmc_nand_data, &gpmc_t);
return 0;
}
#else
static int gpmc_probe_nand_child(struct platform_device *pdev,
struct device_node *child)
{
return 0;
}
#endif
#ifdef CONFIG_MTD_ONENAND
static int gpmc_probe_onenand_child(struct platform_device *pdev,
struct device_node *child)
{
u32 val;
struct omap_onenand_platform_data *gpmc_onenand_data;
if (of_property_read_u32(child, "reg", &val) < 0) {
dev_err(&pdev->dev, "%s has no 'reg' property\n",
child->full_name);
return -ENODEV;
}
gpmc_onenand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_onenand_data),
GFP_KERNEL);
if (!gpmc_onenand_data)
return -ENOMEM;
gpmc_onenand_data->cs = val;
gpmc_onenand_data->of_node = child;
gpmc_onenand_data->dma_channel = -1;
if (!of_property_read_u32(child, "dma-channel", &val))
gpmc_onenand_data->dma_channel = val;
gpmc_onenand_init(gpmc_onenand_data);
return 0;
}
#else
static int gpmc_probe_onenand_child(struct platform_device *pdev,
struct device_node *child)
{
return 0;
}
#endif
static int gpmc_probe_dt(struct platform_device *pdev)
{
int ret;
struct device_node *child;
const struct of_device_id *of_id =
of_match_device(gpmc_dt_ids, &pdev->dev);
if (!of_id)
return 0;
for_each_node_by_name(child, "nand") {
ret = gpmc_probe_nand_child(pdev, child);
if (ret < 0) {
of_node_put(child);
return ret;
}
}
for_each_node_by_name(child, "onenand") {
ret = gpmc_probe_onenand_child(pdev, child);
if (ret < 0) {
of_node_put(child);
return ret;
}
}
return 0;
}
#else
static int gpmc_probe_dt(struct platform_device *pdev)
{
return 0;
}
#endif
static int gpmc_probe(struct platform_device *pdev) static int gpmc_probe(struct platform_device *pdev)
{ {
int rc; int rc;
...@@ -1174,6 +1388,14 @@ static int gpmc_probe(struct platform_device *pdev) ...@@ -1174,6 +1388,14 @@ static int gpmc_probe(struct platform_device *pdev)
if (IS_ERR_VALUE(gpmc_setup_irq())) if (IS_ERR_VALUE(gpmc_setup_irq()))
dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
rc = gpmc_probe_dt(pdev);
if (rc < 0) {
clk_disable_unprepare(gpmc_l3_clk);
clk_put(gpmc_l3_clk);
dev_err(gpmc_dev, "failed to probe DT parameters\n");
return rc;
}
return 0; return 0;
} }
...@@ -1191,6 +1413,7 @@ static struct platform_driver gpmc_driver = { ...@@ -1191,6 +1413,7 @@ static struct platform_driver gpmc_driver = {
.driver = { .driver = {
.name = DEVICE_NAME, .name = DEVICE_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpmc_dt_ids),
}, },
}; };
...@@ -1214,6 +1437,13 @@ static int __init omap_gpmc_init(void) ...@@ -1214,6 +1437,13 @@ static int __init omap_gpmc_init(void)
struct platform_device *pdev; struct platform_device *pdev;
char *oh_name = "gpmc"; char *oh_name = "gpmc";
/*
* if the board boots up with a populated DT, do not
* manually add the device from this initcall
*/
if (of_have_populated_dt())
return -ENODEV;
oh = omap_hwmod_lookup(oh_name); oh = omap_hwmod_lookup(oh_name);
if (!oh) { if (!oh) {
pr_err("Could not look up %s\n", oh_name); pr_err("Could not look up %s\n", oh_name);
......
...@@ -1332,6 +1332,7 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1332,6 +1332,7 @@ static int omap_nand_probe(struct platform_device *pdev)
dma_cap_mask_t mask; dma_cap_mask_t mask;
unsigned sig; unsigned sig;
struct resource *res; struct resource *res;
struct mtd_part_parser_data ppdata = {};
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
if (pdata == NULL) { if (pdata == NULL) {
...@@ -1557,7 +1558,8 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1557,7 +1558,8 @@ static int omap_nand_probe(struct platform_device *pdev)
goto out_release_mem_region; goto out_release_mem_region;
} }
mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, ppdata.of_node = pdata->of_node;
mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts,
pdata->nr_parts); pdata->nr_parts);
platform_set_drvdata(pdev, &info->mtd); platform_set_drvdata(pdev, &info->mtd);
......
...@@ -637,6 +637,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) ...@@ -637,6 +637,7 @@ static int omap2_onenand_probe(struct platform_device *pdev)
struct onenand_chip *this; struct onenand_chip *this;
int r; int r;
struct resource *res; struct resource *res;
struct mtd_part_parser_data ppdata = {};
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
if (pdata == NULL) { if (pdata == NULL) {
...@@ -767,7 +768,8 @@ static int omap2_onenand_probe(struct platform_device *pdev) ...@@ -767,7 +768,8 @@ static int omap2_onenand_probe(struct platform_device *pdev)
if ((r = onenand_scan(&c->mtd, 1)) < 0) if ((r = onenand_scan(&c->mtd, 1)) < 0)
goto err_release_regulator; goto err_release_regulator;
r = mtd_device_parse_register(&c->mtd, NULL, NULL, ppdata.of_node = pdata->of_node;
r = mtd_device_parse_register(&c->mtd, NULL, &ppdata,
pdata ? pdata->parts : NULL, pdata ? pdata->parts : NULL,
pdata ? pdata->nr_parts : 0); pdata ? pdata->nr_parts : 0);
if (r) if (r)
......
...@@ -60,6 +60,8 @@ struct omap_nand_platform_data { ...@@ -60,6 +60,8 @@ struct omap_nand_platform_data {
int devsize; int devsize;
enum omap_ecc ecc_opt; enum omap_ecc ecc_opt;
struct gpmc_nand_regs reg; struct gpmc_nand_regs reg;
};
/* for passing the partitions */
struct device_node *of_node;
};
#endif #endif
...@@ -29,5 +29,8 @@ struct omap_onenand_platform_data { ...@@ -29,5 +29,8 @@ struct omap_onenand_platform_data {
u8 flags; u8 flags;
u8 regulator_can_sleep; u8 regulator_can_sleep;
u8 skip_initial_unlocking; u8 skip_initial_unlocking;
/* for passing the partitions */
struct device_node *of_node;
}; };
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment