[PATCH v5 11/14] ARM: OMAP2+: gpmc: handle connected peripherals
Afzal Mohammed
afzal at ti.com
Mon Jun 11 10:27:28 EDT 2012
Platform will provide driver with configuration details for
each CS like configuration, timing, interrupts. Setup GPMC
based on it. Platform data also provides platform data &
resources used for connected peripheral (eg. gpio irq).
GPMC driver tunnels those information to respective driver.
Signed-off-by: Afzal Mohammed <afzal at ti.com>
---
arch/arm/mach-omap2/gpmc.c | 146 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 146 insertions(+)
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 9073a8a..281bd23 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -155,6 +155,8 @@ struct gpmc_peripheral {
struct platform_device *pdev;
};
+static struct gpmc_peripheral gpmc_peripheral[GPMC_CS_NUM];
+static unsigned gpmc_num_peripheral;
static unsigned gpmc_waitpin_map;
static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
@@ -1235,6 +1237,39 @@ static int gpmc_setup_cs_waitpin(struct gpmc_peripheral *g_per, unsigned cs,
return 0;
}
+static int gpmc_setup_cs_config_timing(struct gpmc_peripheral *g_per,
+ struct gpmc_cs_data *cs)
+{
+ int ret;
+
+ /* some boards rely on bootloader for configuration */
+ if (cs->have_config) {
+ gpmc_setup_cs_config(cs->cs, cs->config);
+ ret = gpmc_setup_cs_waitpin(g_per, cs->cs, cs->config);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(gpmc_dev, "error: waitpin on CS %d\n", cs->cs);
+ return ret;
+ }
+ } else
+ gpmc_print_cs_config(cs->cs);
+
+ /* some boards rely on bootloader for timing */
+ if (cs->time_ctrl.type == has_period) {
+ ret = gpmc_cs_set_timings(cs->cs, &cs->time_ctrl.timings);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(gpmc_dev, "error: timing on CS: %d\n", cs->cs);
+ return ret;
+ }
+ gpmc_cs_misc_timings(cs->cs, &cs->time_ctrl.bool_timings);
+ } else if (cs->time_ctrl.type == has_clock) {
+ gpmc_cs_set_register_timings(cs->cs, &cs->time_ctrl.timings);
+ gpmc_cs_misc_timings(cs->cs, &cs->time_ctrl.bool_timings);
+ } else
+ gpmc_print_cs_timings(cs->cs);
+
+ return 0;
+}
+
static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
{
return bitmask;
@@ -1307,10 +1342,100 @@ static int gpmc_setup_waitpin(struct gpmc_peripheral *g_per)
return 0;
}
+static __devinit int gpmc_setup_cs(struct gpmc_peripheral *g_per,
+ struct gpmc_cs_data *cs, struct resource *res)
+{
+ int num, ret;
+
+ num = gpmc_setup_cs_mem(cs, res);
+ if (IS_ERR_VALUE(num))
+ return num;
+
+ ret = gpmc_setup_cs_config_timing(g_per, cs);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ num += gpmc_setup_cs_irq(cs, res + num);
+
+ return num;
+}
+
+static __devinit int gpmc_setup_device(struct gpmc_peripheral *g_per,
+ struct gpmc_device_pdata *gdp)
+{
+ int i, n, ret;
+ struct gpmc_cs_data *cs;
+
+ for (i = 0, n = gdp->num_cs, cs = gdp->cs_data;
+ i < gdp->num_cs; i++, cs++)
+ n += hweight32(cs->irq_config);
+
+ g_per->gpmc_res = devm_kzalloc(gpmc_dev, sizeof(*g_per->gpmc_res) * n,
+ GFP_KERNEL);
+ if (g_per->gpmc_res == NULL) {
+ dev_err(gpmc_dev, "error: memory allocation\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0, cs = gdp->cs_data, g_per->gpmc_res_cnt = 0;
+ i < gdp->num_cs; cs++, i++) {
+ ret = gpmc_setup_cs(g_per, cs,
+ g_per->gpmc_res + g_per->gpmc_res_cnt);
+ if (IS_ERR_VALUE(ret) ||
+ IS_ERR_VALUE(gpmc_setup_waitpin(g_per))) {
+ dev_err(gpmc_dev, "error: setup for %s\n", gdp->name);
+ devm_kfree(gpmc_dev, g_per->gpmc_res);
+ g_per->gpmc_res = NULL;
+ g_per->gpmc_res_cnt = 0;
+ return -EINVAL;
+ } else
+ g_per->gpmc_res_cnt += ret;
+ }
+
+ g_per->name = gdp->name;
+ g_per->id = gdp->id;
+ g_per->pdata = gdp->pdata;
+ g_per->pdata_size = gdp->pdata_size;
+ g_per->per_res = gdp->per_res;
+ g_per->per_res_cnt = gdp->per_res_cnt;
+
+ return 0;
+}
+
+static __devinit
+struct platform_device *gpmc_create_device(struct gpmc_peripheral *p)
+{
+ int num = p->per_res_cnt + p->gpmc_res_cnt;
+ struct resource *res;
+ struct platform_device *pdev;
+
+ res = devm_kzalloc(gpmc_dev, sizeof(struct resource) * num,
+ GFP_KERNEL);
+ if (!res) {
+ dev_err(gpmc_dev, "error: allocating memory\n");
+ return NULL;
+ }
+
+ memcpy((char *)res, (const char *) p->gpmc_res,
+ sizeof(struct resource) * p->gpmc_res_cnt);
+ memcpy((char *)(res + p->gpmc_res_cnt), (const char *)p->per_res,
+ sizeof(struct resource) * p->per_res_cnt);
+
+ p->pdev = platform_device_register_resndata(gpmc_dev, p->name, p->id,
+ res, num, p->pdata, p->pdata_size);
+
+ devm_kfree(gpmc_dev, res);
+
+ return pdev;
+}
+
static __devinit int gpmc_probe(struct platform_device *pdev)
{
u32 l;
struct resource *res;
+ struct gpmc_pdata *gp = dev_get_platdata(&pdev->dev);
+ struct gpmc_device_pdata **gdq = NULL;
+ struct gpmc_peripheral *g_per = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
@@ -1342,11 +1467,32 @@ static __devinit int gpmc_probe(struct platform_device *pdev)
if (IS_ERR_VALUE(gpmc_setup_irq()))
dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
+ /* Traverse NULL terminated array of peripheral pointers and setup */
+ for (gdq = gp->device_pdata, g_per = gpmc_peripheral; *gdq; gdq++)
+ if (IS_ERR_VALUE(gpmc_setup_device(g_per, *gdq)))
+ dev_err(gpmc_dev, "gpmc setup on %s failed\n",
+ (*gdq)->name);
+ else
+ g_per++;
+ gpmc_num_peripheral = g_per - gpmc_peripheral;
+
+ for (l = 0, g_per = gpmc_peripheral;
+ l < gpmc_num_peripheral; l++, g_per++)
+ if (IS_ERR(gpmc_create_device(g_per)))
+ dev_err(gpmc_dev, "device creation on %s failed\n",
+ g_per->name);
+
return 0;
}
static __exit int gpmc_remove(struct platform_device *pdev)
{
+ struct gpmc_peripheral *g_per = gpmc_peripheral;
+
+ for (; gpmc_num_peripheral; g_per++, gpmc_num_peripheral--)
+ platform_device_unregister(g_per->pdev);
+
+ gpmc_waitpin_map = 0;
gpmc_free_irq();
gpmc_mem_exit();
gpmc_dev = NULL;
--
1.7.10.2
More information about the linux-arm-kernel
mailing list