* [PATCH] IB/ehca: Serialize HCA-related hCalls if necessary
From: Joachim Fenkes @ 2007-12-10 17:59 UTC (permalink / raw)
To: LinuxPPC-Dev, LKML, OF-General, Roland Dreier, OF-EWG,
Arnd Bergmann
Cc: Stefan Roscher, Christoph Raisch, Marcus Eder
Several pSeries firmware versions share a rare locking issue in the
HCA-related hCalls. Check for a feature flag that indicates the issue being
fixed and serialize all HCA hCalls if not.
Signed-off-by: Joachim Fenkes <fenkes@de.ibm.com>
---
This is the revised version of my previous patch, which does not rely on the
processor version any longer.
drivers/infiniband/hw/ehca/ehca_main.c | 13 +++++++++++++
drivers/infiniband/hw/ehca/hcp_if.c | 28 +++++++++++-----------------
drivers/infiniband/hw/ehca/hipz_hw.h | 1 +
3 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 90d4334..c7bff3e 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -43,6 +43,7 @@
#ifdef CONFIG_PPC_64K_PAGES
#include <linux/slab.h>
#endif
+
#include "ehca_classes.h"
#include "ehca_iverbs.h"
#include "ehca_mrmw.h"
@@ -66,6 +67,7 @@ int ehca_poll_all_eqs = 1;
int ehca_static_rate = -1;
int ehca_scaling_code = 0;
int ehca_mr_largepage = 1;
+int ehca_lock_hcalls = -1;
module_param_named(open_aqp1, ehca_open_aqp1, int, S_IRUGO);
module_param_named(debug_level, ehca_debug_level, int, S_IRUGO);
@@ -77,6 +79,7 @@ module_param_named(poll_all_eqs, ehca_poll_all_eqs, int, S_IRUGO);
module_param_named(static_rate, ehca_static_rate, int, S_IRUGO);
module_param_named(scaling_code, ehca_scaling_code, int, S_IRUGO);
module_param_named(mr_largepage, ehca_mr_largepage, int, S_IRUGO);
+module_param_named(lock_hcalls, ehca_lock_hcalls, bool, S_IRUGO);
MODULE_PARM_DESC(open_aqp1,
"AQP1 on startup (0: no (default), 1: yes)");
@@ -102,6 +105,9 @@ MODULE_PARM_DESC(scaling_code,
MODULE_PARM_DESC(mr_largepage,
"use large page for MR (0: use PAGE_SIZE (default), "
"1: use large page depending on MR size");
+MODULE_PARM_DESC(lock_hcalls,
+ "serialize all hCalls made by the driver "
+ "(default: autodetect)");
DEFINE_RWLOCK(ehca_qp_idr_lock);
DEFINE_RWLOCK(ehca_cq_idr_lock);
@@ -258,6 +264,7 @@ static struct cap_descr {
{ HCA_CAP_UD_LL_QP, "HCA_CAP_UD_LL_QP" },
{ HCA_CAP_RESIZE_MR, "HCA_CAP_RESIZE_MR" },
{ HCA_CAP_MINI_QP, "HCA_CAP_MINI_QP" },
+ { HCA_CAP_H_ALLOC_RES_SYNC, "HCA_CAP_H_ALLOC_RES_SYNC" },
};
static int ehca_sense_attributes(struct ehca_shca *shca)
@@ -333,6 +340,12 @@ static int ehca_sense_attributes(struct ehca_shca *shca)
if (EHCA_BMASK_GET(hca_cap_descr[i].mask, shca->hca_cap))
ehca_gen_dbg(" %s", hca_cap_descr[i].descr);
+ /* Autodetect hCall locking -- the "H_ALLOC_RESOURCE synced" flag is
+ * a firmware property, so it's valid across all adapters
+ */
+ if (ehca_lock_hcalls == -1)
+ ehca_lock_hcalls = !(shca->hca_cap & HCA_CAP_H_ALLOC_RES_SYNC);
+
/* translate supported MR page sizes; always support 4K */
shca->hca_cap_mr_pgsize = EHCA_PAGESIZE;
if (ehca_mr_largepage) { /* support extra sizes only if enabled */
diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c
index c16a213..331b5e8 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.c
+++ b/drivers/infiniband/hw/ehca/hcp_if.c
@@ -89,6 +89,7 @@
#define HCALL9_REGS_FORMAT HCALL7_REGS_FORMAT " r11=%lx r12=%lx"
static DEFINE_SPINLOCK(hcall_lock);
+extern int ehca_lock_hcalls;
static u32 get_longbusy_msecs(int longbusy_rc)
{
@@ -120,26 +121,21 @@ static long ehca_plpar_hcall_norets(unsigned long opcode,
unsigned long arg7)
{
long ret;
- int i, sleep_msecs, do_lock;
- unsigned long flags;
+ int i, sleep_msecs;
+ unsigned long flags = 0;
ehca_gen_dbg("opcode=%lx " HCALL7_REGS_FORMAT,
opcode, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
- /* lock H_FREE_RESOURCE(MR) against itself and H_ALLOC_RESOURCE(MR) */
- if ((opcode == H_FREE_RESOURCE) && (arg7 == 5)) {
- arg7 = 0; /* better not upset firmware */
- do_lock = 1;
- }
-
for (i = 0; i < 5; i++) {
- if (do_lock)
+ /* serialize hCalls to work around firmware issue */
+ if (ehca_lock_hcalls)
spin_lock_irqsave(&hcall_lock, flags);
ret = plpar_hcall_norets(opcode, arg1, arg2, arg3, arg4,
arg5, arg6, arg7);
- if (do_lock)
+ if (ehca_lock_hcalls)
spin_unlock_irqrestore(&hcall_lock, flags);
if (H_IS_LONG_BUSY(ret)) {
@@ -174,24 +170,22 @@ static long ehca_plpar_hcall9(unsigned long opcode,
unsigned long arg9)
{
long ret;
- int i, sleep_msecs, do_lock;
+ int i, sleep_msecs;
unsigned long flags = 0;
ehca_gen_dbg("INPUT -- opcode=%lx " HCALL9_REGS_FORMAT, opcode,
arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
- /* lock H_ALLOC_RESOURCE(MR) against itself and H_FREE_RESOURCE(MR) */
- do_lock = ((opcode == H_ALLOC_RESOURCE) && (arg2 == 5));
-
for (i = 0; i < 5; i++) {
- if (do_lock)
+ /* serialize hCalls to work around firmware issue */
+ if (ehca_lock_hcalls)
spin_lock_irqsave(&hcall_lock, flags);
ret = plpar_hcall9(opcode, outs,
arg1, arg2, arg3, arg4, arg5,
arg6, arg7, arg8, arg9);
- if (do_lock)
+ if (ehca_lock_hcalls)
spin_unlock_irqrestore(&hcall_lock, flags);
if (H_IS_LONG_BUSY(ret)) {
@@ -821,7 +815,7 @@ u64 hipz_h_free_resource_mr(const struct ipz_adapter_handle adapter_handle,
return ehca_plpar_hcall_norets(H_FREE_RESOURCE,
adapter_handle.handle, /* r4 */
mr->ipz_mr_handle.handle, /* r5 */
- 0, 0, 0, 0, 5);
+ 0, 0, 0, 0, 0);
}
u64 hipz_h_reregister_pmr(const struct ipz_adapter_handle adapter_handle,
diff --git a/drivers/infiniband/hw/ehca/hipz_hw.h b/drivers/infiniband/hw/ehca/hipz_hw.h
index 485b840..bf996c7 100644
--- a/drivers/infiniband/hw/ehca/hipz_hw.h
+++ b/drivers/infiniband/hw/ehca/hipz_hw.h
@@ -378,6 +378,7 @@ struct hipz_query_hca {
#define HCA_CAP_UD_LL_QP EHCA_BMASK_IBM(16, 16)
#define HCA_CAP_RESIZE_MR EHCA_BMASK_IBM(17, 17)
#define HCA_CAP_MINI_QP EHCA_BMASK_IBM(18, 18)
+#define HCA_CAP_H_ALLOC_RES_SYNC EHCA_BMASK_IBM(19, 19)
/* query port response block */
struct hipz_query_port {
--
1.5.2
^ permalink raw reply related
* Re: [PATCH 4/25] powerpc: Reworking machine check handling and Fix 440/440A
From: Josh Boyer @ 2007-12-10 17:59 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <20071206080114.CDB13DDE33@ozlabs.org>
On Thu, 06 Dec 2007 19:00:03 +1100
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> This adds a cputable function pointer for the CPU-side machine
> check handling. The semantic is still the same as the old one,
> the one in ppc_md. overrides the one in cputable, though
> ultimately we'll want to change that so the CPU gets first.
>
> This removes CONFIG_440A which was a problem for multiplatform
> kernels and instead fixes up the IVOR at runtime from a setup_cpu
> function. The "A" version of the machine check also tweaks the
> regs->trap value to differenciate the 2 versions at the C level.
This breaks ARCH=ppc builds. Unfortunately, that tree shares the
cputable.[ch] files, but has it's own traps.c. Which means you get
lots of nice undefined references like below for example:
arch/powerpc/kernel/built-in.o:(.init.data+0x44): undefined reference to `machine_check_4xx'
arch/powerpc/kernel/built-in.o:(.init.data+0x8c): undefined reference to `machine_check_4xx'
arch/powerpc/kernel/built-in.o:(.init.data+0xd4): undefined reference to `machine_check_4xx'
Because the cputable entries for the processors are setting the .machine_check function and it's never built.
I'm not sure which would be easier, making arch/ppc use traps.c from arch/powerpc, or adding similar functionality there.
josh
^ permalink raw reply
* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Jon Smirl @ 2007-12-10 18:06 UTC (permalink / raw)
To: Scott Wood; +Cc: Jean Delvare, i2c, linuxppc-dev
In-Reply-To: <20071210164255.GA4497@loki.buserror.net>
On 12/10/07, Scott Wood <scottwood@freescale.com> wrote:
> On Mon, Dec 10, 2007 at 08:38:46AM +1100, Benjamin Herrenschmidt wrote:
> > The more I think about it, the more I tend to agree that tagging isn't
> > necessary and you are right. We should just match the name against the
> > "compatible" property of the OF nodes (which mean we need to support
> > multiple matches though since "compatible" is a list of strings).
>
> It may not be strictly necessary, but I think it's a good idea not just for
> safety reasons, but as an indication to the driver what additional
> information it has access to. We could put a match data pointer in the i2c
> device, and have it be a valid node pointer if the match was an OF one (and
> a device-specific struct for a straight platform device, etc). This could
> be useful if a device needs to have more properties than standard
> address/type/interrupt for some reason.
I can't see an easy way to do this. The basic problem is that the i2c
drivers are assumed to be cross platform. I would need to add a path
through the i2c core for getting a void pointer from the bus to the
device But then when the device code gets this pointer it has no way
of knowing what it was. Assuming the void is a pointer to an of_node
would make the driver for the i2c device platform specific.
Another way that would work cross platform would be for the module to
have module parameters for the extra attributes. The bus code could
then look in the of_node for extra attributes and use them to set the
module parameters. I know this can be done, but doing it is a little
above my understanding of the module code. You need to ask the module
it's parameter names, addresses and types and then match them to
attributes in the of_node and do the copy.
>
> -Scott
>
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* [PATCH 2/5] Modify several rtc drivers to use the alias names list property of i2c
From: Jon Smirl @ 2007-12-10 18:33 UTC (permalink / raw)
To: i2c, linuxppc-dev
In-Reply-To: <20071210183323.12584.88127.stgit@terra.home>
This patch modifies the ds1307, ds1374, and rs5c372 i2c drivers to support
device tree names using the new i2c mod alias support
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
arch/powerpc/sysdev/fsl_soc.c | 44 ++++++-----------------------------------
drivers/rtc/rtc-ds1307.c | 38 +++++++++++++++++++----------------
drivers/rtc/rtc-ds1374.c | 11 +++++++++-
drivers/rtc/rtc-rs5c372.c | 31 ++++++++++++++++-------------
4 files changed, 53 insertions(+), 71 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 3ace747..268638a 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -320,48 +320,12 @@ arch_initcall(gfar_of_init);
#ifdef CONFIG_I2C_BOARDINFO
#include <linux/i2c.h>
-struct i2c_driver_device {
- char *of_device;
- char *i2c_driver;
- char *i2c_type;
-};
-
-static struct i2c_driver_device i2c_devices[] __initdata = {
- {"ricoh,rs5c372a", "rtc-rs5c372", "rs5c372a",},
- {"ricoh,rs5c372b", "rtc-rs5c372", "rs5c372b",},
- {"ricoh,rv5c386", "rtc-rs5c372", "rv5c386",},
- {"ricoh,rv5c387a", "rtc-rs5c372", "rv5c387a",},
- {"dallas,ds1307", "rtc-ds1307", "ds1307",},
- {"dallas,ds1337", "rtc-ds1307", "ds1337",},
- {"dallas,ds1338", "rtc-ds1307", "ds1338",},
- {"dallas,ds1339", "rtc-ds1307", "ds1339",},
- {"dallas,ds1340", "rtc-ds1307", "ds1340",},
- {"stm,m41t00", "rtc-ds1307", "m41t00"},
- {"dallas,ds1374", "rtc-ds1374", "rtc-ds1374",},
-};
-
-static int __init of_find_i2c_driver(struct device_node *node,
- struct i2c_board_info *info)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) {
- if (!of_device_is_compatible(node, i2c_devices[i].of_device))
- continue;
- if (strlcpy(info->driver_name, i2c_devices[i].i2c_driver,
- KOBJ_NAME_LEN) >= KOBJ_NAME_LEN ||
- strlcpy(info->type, i2c_devices[i].i2c_type,
- I2C_NAME_SIZE) >= I2C_NAME_SIZE)
- return -ENOMEM;
- return 0;
- }
- return -ENODEV;
-}
static void __init of_register_i2c_devices(struct device_node *adap_node,
int bus_num)
{
struct device_node *node = NULL;
+ const char *compatible;
while ((node = of_get_next_child(adap_node, node))) {
struct i2c_board_info info = {};
@@ -378,8 +342,12 @@ static void __init of_register_i2c_devices(struct device_node *adap_node,
if (info.irq == NO_IRQ)
info.irq = -1;
- if (of_find_i2c_driver(node, &info) < 0)
+ compatible = of_get_property(node, "compatible", &len);
+ if (!compatible) {
+ printk(KERN_WARNING "i2c-mpc.c: invalid entry, missing compatible attribute\n");
continue;
+ }
+ strncpy(info.driver_name, compatible, sizeof(info.driver_name));
info.addr = *addr;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index bc1c7fe..a4dec4b 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -99,45 +99,46 @@ struct ds1307 {
};
struct chip_desc {
- char name[9];
unsigned nvram56:1;
unsigned alarm:1;
enum ds_type type;
};
static const struct chip_desc chips[] = { {
- .name = "ds1307",
.type = ds_1307,
.nvram56 = 1,
}, {
- .name = "ds1337",
.type = ds_1337,
.alarm = 1,
}, {
- .name = "ds1338",
.type = ds_1338,
.nvram56 = 1,
}, {
- .name = "ds1339",
.type = ds_1339,
.alarm = 1,
}, {
- .name = "ds1340",
.type = ds_1340,
}, {
- .name = "m41t00",
.type = m41t00,
}, };
-static inline const struct chip_desc *find_chip(const char *s)
-{
- unsigned i;
-
- for (i = 0; i < ARRAY_SIZE(chips); i++)
- if (strnicmp(s, chips[i].name, sizeof chips[i].name) == 0)
- return &chips[i];
- return NULL;
-}
+static struct i2c_device_id ds1307_id[] = {
+ {"rtc-ds1307", ds_1307},
+ {"ds1307", ds_1307},
+ {"ds1337", ds_1337},
+ {"ds1338", ds_1338},
+ {"ds1339", ds_1339},
+ {"ds1340", ds_1340},
+ {"m41t00", m41t00},
+ {"dallas,ds1307", ds_1307},
+ {"dallas,ds1337", ds_1337},
+ {"dallas,ds1338", ds_1338},
+ {"dallas,ds1339", ds_1339},
+ {"dallas,ds1340", ds_1340},
+ {"stm,m41t00", m41t00},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ds1307_id);
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
{
@@ -326,7 +327,7 @@ static struct bin_attribute nvram = {
static struct i2c_driver ds1307_driver;
-static int __devinit ds1307_probe(struct i2c_client *client)
+static int __devinit ds1307_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct ds1307 *ds1307;
int err = -ENODEV;
@@ -334,7 +335,7 @@ static int __devinit ds1307_probe(struct i2c_client *client)
const struct chip_desc *chip;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- chip = find_chip(client->name);
+ chip = &chips[id->driver_data];
if (!chip) {
dev_err(&client->dev, "unknown chip type '%s'\n",
client->name);
@@ -537,6 +538,7 @@ static struct i2c_driver ds1307_driver = {
},
.probe = ds1307_probe,
.remove = __devexit_p(ds1307_remove),
+ .id_table = ds1307_id,
};
static int __init ds1307_init(void)
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 45bda18..2b852f3 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -41,6 +41,14 @@
#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
#define DS1374_REG_TCR 0x09 /* Trickle Charge */
+static struct i2c_device_id ds1374_id[] = {
+ {"rtc-ds1374", 0},
+ {"ds1374", 0},
+ {"dallas,ds1374", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ds1374_id);
+
struct ds1374 {
struct i2c_client *client;
struct rtc_device *rtc;
@@ -355,7 +363,7 @@ static const struct rtc_class_ops ds1374_rtc_ops = {
.ioctl = ds1374_ioctl,
};
-static int ds1374_probe(struct i2c_client *client)
+static int ds1374_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct ds1374 *ds1374;
int ret;
@@ -429,6 +437,7 @@ static struct i2c_driver ds1374_driver = {
},
.probe = ds1374_probe,
.remove = __devexit_p(ds1374_remove),
+ .id_table = ds1374_id,
};
static int __init ds1374_init(void)
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index 6b67b50..de0d458 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -62,13 +62,26 @@
enum rtc_type {
- rtc_undef = 0,
rtc_rs5c372a,
rtc_rs5c372b,
rtc_rv5c386,
rtc_rv5c387a,
};
+static struct i2c_device_id rs5c372_id[] = {
+ {"rtc-rs5c372", rtc_rs5c372a},
+ {"rs5c372a", rtc_rs5c372a},
+ {"rs5c372b", rtc_rs5c372b},
+ {"rv5c386", rtc_rv5c386},
+ {"rv5c387a", rtc_rv5c387a},
+ {"ricoh,rs5c372a", rtc_rs5c372a},
+ {"ricoh,rs5c372b", rtc_rs5c372b},
+ {"ricoh,rv5c386", rtc_rv5c386},
+ {"ricoh,rv5c387a", rtc_rv5c387a},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, rs5c372_id);
+
/* REVISIT: this assumes that:
* - we're in the 21st century, so it's safe to ignore the century
* bit for rv5c38[67] (REG_MONTH bit 7);
@@ -494,7 +507,7 @@ static void rs5c_sysfs_unregister(struct device *dev)
static struct i2c_driver rs5c372_driver;
-static int rs5c372_probe(struct i2c_client *client)
+static int rs5c372_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int err = 0;
struct rs5c372 *rs5c372;
@@ -522,18 +535,7 @@ static int rs5c372_probe(struct i2c_client *client)
if (err < 0)
goto exit_kfree;
- if (strcmp(client->name, "rs5c372a") == 0)
- rs5c372->type = rtc_rs5c372a;
- else if (strcmp(client->name, "rs5c372b") == 0)
- rs5c372->type = rtc_rs5c372b;
- else if (strcmp(client->name, "rv5c386") == 0)
- rs5c372->type = rtc_rv5c386;
- else if (strcmp(client->name, "rv5c387a") == 0)
- rs5c372->type = rtc_rv5c387a;
- else {
- rs5c372->type = rtc_rs5c372b;
- dev_warn(&client->dev, "assuming rs5c372b\n");
- }
+ rs5c372->type = id->driver_data;
/* clock may be set for am/pm or 24 hr time */
switch (rs5c372->type) {
@@ -651,6 +653,7 @@ static struct i2c_driver rs5c372_driver = {
},
.probe = rs5c372_probe,
.remove = rs5c372_remove,
+ .id_table = rs5c372_id,
};
static __init int rs5c372_init(void)
^ permalink raw reply related
* [PATCH 3/5] Clean up error returns
From: Jon Smirl @ 2007-12-10 18:33 UTC (permalink / raw)
To: i2c, linuxppc-dev
In-Reply-To: <20071210183323.12584.88127.stgit@terra.home>
Return errors that were being ignored in the mpc-i2c driver
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
drivers/i2c/busses/i2c-mpc.c | 30 +++++++++++++++++-------------
1 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index d8de4ac..7c35a8f 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -180,7 +180,7 @@ static void mpc_i2c_stop(struct mpc_i2c *i2c)
static int mpc_write(struct mpc_i2c *i2c, int target,
const u8 * data, int length, int restart)
{
- int i;
+ int i, result;
unsigned timeout = i2c->adap.timeout;
u32 flags = restart ? CCR_RSTA : 0;
@@ -192,15 +192,17 @@ static int mpc_write(struct mpc_i2c *i2c, int target,
/* Write target byte */
writeb((target << 1), i2c->base + MPC_I2C_DR);
- if (i2c_wait(i2c, timeout, 1) < 0)
- return -1;
+ result = i2c_wait(i2c, timeout, 1);
+ if (result < 0)
+ return result;
for (i = 0; i < length; i++) {
/* Write data byte */
writeb(data[i], i2c->base + MPC_I2C_DR);
- if (i2c_wait(i2c, timeout, 1) < 0)
- return -1;
+ result = i2c_wait(i2c, timeout, 1);
+ if (result < 0)
+ return result;
}
return 0;
@@ -210,7 +212,7 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
u8 * data, int length, int restart)
{
unsigned timeout = i2c->adap.timeout;
- int i;
+ int i, result;
u32 flags = restart ? CCR_RSTA : 0;
/* Start with MEN */
@@ -221,8 +223,9 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
/* Write target address byte - this time with the read flag set */
writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
- if (i2c_wait(i2c, timeout, 1) < 0)
- return -1;
+ result = i2c_wait(i2c, timeout, 1);
+ if (result < 0)
+ return result;
if (length) {
if (length == 1)
@@ -234,8 +237,9 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
}
for (i = 0; i < length; i++) {
- if (i2c_wait(i2c, timeout, 0) < 0)
- return -1;
+ result = i2c_wait(i2c, timeout, 0);
+ if (result < 0)
+ return result;
/* Generate txack on next to last byte */
if (i == length - 2)
@@ -321,9 +325,9 @@ static int fsl_i2c_probe(struct platform_device *pdev)
pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
- if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) {
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
return -ENOMEM;
- }
i2c->irq = platform_get_irq(pdev, 0);
if (i2c->irq < 0) {
@@ -381,7 +385,7 @@ static int fsl_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&i2c->adap);
platform_set_drvdata(pdev, NULL);
- if (i2c->irq != 0)
+ if (i2c->irq != NO_IRQ)
free_irq(i2c->irq, i2c);
iounmap(i2c->base);
^ permalink raw reply related
* [PATCH 1/5] Implement module aliasing for i2c to translate from device tree names
From: Jon Smirl @ 2007-12-10 18:33 UTC (permalink / raw)
To: i2c, linuxppc-dev
In-Reply-To: <20071210183323.12584.88127.stgit@terra.home>
This patch allows new style i2c chip drivers to have alias names using
the official kernel aliasing system and MODULE_DEVICE_TABLE(). I've
tested it on PowerPC and x86. This change is required for PowerPC
device tree support.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
drivers/i2c/i2c-core.c | 34 +++++++++++++++++++++++++++-------
include/linux/i2c.h | 5 ++---
include/linux/mod_devicetable.h | 9 +++++++++
3 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index b5e13e4..6fa6bab 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -47,10 +47,26 @@ static DEFINE_IDR(i2c_adapter_idr);
/* ------------------------------------------------------------------------- */
-static int i2c_device_match(struct device *dev, struct device_driver *drv)
+static const struct i2c_device_id *i2c_device_match(const struct i2c_device_id *id, struct i2c_client *client)
+{
+ /* new style drivers use the same kind of driver matching policy
+ * as platform devices or SPI: compare device and driver IDs.
+ */
+ if (id) {
+ while (id->name[0]) {
+ if (strcmp(client->driver_name, id->name) == 0)
+ return id;
+ id++;
+ }
+ }
+ return NULL;
+}
+
+static int i2c_bus_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(drv);
+ const struct i2c_device_id *found_id;
/* make legacy i2c drivers bypass driver model probing entirely;
* such drivers scan each i2c adapter/bus themselves.
@@ -58,10 +74,11 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
if (!is_newstyle_driver(driver))
return 0;
- /* new style drivers use the same kind of driver matching policy
- * as platform devices or SPI: compare device and driver IDs.
- */
- return strcmp(client->driver_name, drv->name) == 0;
+ found_id = i2c_device_match(driver->id_table, client);
+ if (found_id)
+ return 1;
+
+ return 0;
}
#ifdef CONFIG_HOTPLUG
@@ -89,12 +106,15 @@ static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
+ const struct i2c_device_id *id;
if (!driver->probe)
return -ENODEV;
client->driver = driver;
dev_dbg(dev, "probe\n");
- return driver->probe(client);
+
+ id = i2c_device_match(driver->id_table, client);
+ return driver->probe(client, id);
}
static int i2c_device_remove(struct device *dev)
@@ -189,7 +209,7 @@ static struct device_attribute i2c_dev_attrs[] = {
static struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
- .match = i2c_device_match,
+ .match = i2c_bus_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index a100c9f..0ca1a59 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -126,7 +126,7 @@ struct i2c_driver {
* With the driver model, device enumeration is NEVER done by drivers;
* it's done by infrastructure. (NEW STYLE DRIVERS ONLY)
*/
- int (*probe)(struct i2c_client *);
+ int (*probe)(struct i2c_client *, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
@@ -141,11 +141,10 @@ struct i2c_driver {
struct device_driver driver;
struct list_head list;
+ struct i2c_device_id *id_table;
};
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
-#define I2C_NAME_SIZE 20
-
/**
* struct i2c_client - represent an I2C slave device
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index e9fddb4..688fad6 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -367,4 +367,13 @@ struct virtio_device_id {
};
#define VIRTIO_DEV_ANY_ID 0xffffffff
+/* i2c */
+
+#define I2C_NAME_SIZE 40
+struct i2c_device_id {
+ char name[I2C_NAME_SIZE];
+ kernel_ulong_t driver_data; /* Data private to the driver */
+};
+
+
#endif /* LINUX_MOD_DEVICETABLE_H */
^ permalink raw reply related
* [PATCH 5/5] Convert pfc8563 i2c driver from old style to new style
From: Jon Smirl @ 2007-12-10 18:33 UTC (permalink / raw)
To: i2c, linuxppc-dev
In-Reply-To: <20071210183323.12584.88127.stgit@terra.home>
Convert pfc8563 i2c driver from old style to new style. The
driver is also modified to support device tree names via the
i2c mod alias mechanism.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
drivers/rtc/rtc-pcf8563.c | 110 ++++++++++++---------------------------------
1 files changed, 30 insertions(+), 80 deletions(-)
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 0242d80..7da2cd0 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -25,10 +25,6 @@
* located at 0x51 will pass the validation routine due to
* the way the registers are implemented.
*/
-static unsigned short normal_i2c[] = { I2C_CLIENT_END };
-
-/* Module parameters */
-I2C_CLIENT_INSMOD;
#define PCF8563_REG_ST1 0x00 /* status */
#define PCF8563_REG_ST2 0x01
@@ -72,9 +68,6 @@ struct pcf8563 {
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
};
-static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind);
-static int pcf8563_detach(struct i2c_client *client);
-
/*
* In the routines that deal directly with the pcf8563 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
@@ -257,98 +250,55 @@ static const struct rtc_class_ops pcf8563_rtc_ops = {
.set_time = pcf8563_rtc_set_time,
};
-static int pcf8563_attach(struct i2c_adapter *adapter)
+static int pcf8563_remove(struct i2c_client *client)
{
- return i2c_probe(adapter, &addr_data, pcf8563_probe);
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ return 0;
}
+static struct i2c_device_id pcf8563_id[] = {
+ {"rtc-pcf8563", 0},
+ {"pcf8563", 0},
+ {"philips,pcf8563", 0},
+ {"rtc8564", 0},
+ {"epson,rtc8564", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pcf8563_id);
+
+static int pcf8563_probe(struct i2c_client *client, const struct i2c_device_id *id);
+
static struct i2c_driver pcf8563_driver = {
.driver = {
- .name = "pcf8563",
+ .name = "rtc-pcf8563",
},
.id = I2C_DRIVERID_PCF8563,
- .attach_adapter = &pcf8563_attach,
- .detach_client = &pcf8563_detach,
+ .probe = &pcf8563_probe,
+ .remove = &pcf8563_remove,
+ .id_table = pcf8563_id,
};
-static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
+static int pcf8563_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
- struct pcf8563 *pcf8563;
- struct i2c_client *client;
+ int result;
struct rtc_device *rtc;
- int err = 0;
-
- dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
- err = -ENODEV;
- goto exit;
- }
-
- if (!(pcf8563 = kzalloc(sizeof(struct pcf8563), GFP_KERNEL))) {
- err = -ENOMEM;
- goto exit;
- }
-
- client = &pcf8563->client;
- client->addr = address;
- client->driver = &pcf8563_driver;
- client->adapter = adapter;
-
- strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE);
-
- /* Verify the chip is really an PCF8563 */
- if (kind < 0) {
- if (pcf8563_validate_client(client) < 0) {
- err = -ENODEV;
- goto exit_kfree;
- }
- }
-
- /* Inform the i2c layer */
- if ((err = i2c_attach_client(client)))
- goto exit_kfree;
-
- dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+ result = pcf8563_validate_client(client);
+ if (result)
+ return result;
rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev,
&pcf8563_rtc_ops, THIS_MODULE);
-
- if (IS_ERR(rtc)) {
- err = PTR_ERR(rtc);
- goto exit_detach;
- }
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
i2c_set_clientdata(client, rtc);
return 0;
-
-exit_detach:
- i2c_detach_client(client);
-
-exit_kfree:
- kfree(pcf8563);
-
-exit:
- return err;
-}
-
-static int pcf8563_detach(struct i2c_client *client)
-{
- struct pcf8563 *pcf8563 = container_of(client, struct pcf8563, client);
- int err;
- struct rtc_device *rtc = i2c_get_clientdata(client);
-
- if (rtc)
- rtc_device_unregister(rtc);
-
- if ((err = i2c_detach_client(client)))
- return err;
-
- kfree(pcf8563);
-
- return 0;
}
static int __init pcf8563_init(void)
^ permalink raw reply related
* [PATCH 4/5] Convert PowerPC MPC i2c to of_platform_driver from platform_driver
From: Jon Smirl @ 2007-12-10 18:33 UTC (permalink / raw)
To: i2c, linuxppc-dev
In-Reply-To: <20071210183323.12584.88127.stgit@terra.home>
Convert MPC i2c driver from being a platform_driver to an open firmware version. Error returns were improved. Routine names were changed from fsl_ to mpc_ to make them match the file name.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
arch/powerpc/sysdev/fsl_soc.c | 96 -------------------------
drivers/i2c/busses/i2c-mpc.c | 158 +++++++++++++++++++++++++++++------------
2 files changed, 110 insertions(+), 144 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 268638a..d6ef264 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -318,102 +318,6 @@ err:
arch_initcall(gfar_of_init);
-#ifdef CONFIG_I2C_BOARDINFO
-#include <linux/i2c.h>
-
-static void __init of_register_i2c_devices(struct device_node *adap_node,
- int bus_num)
-{
- struct device_node *node = NULL;
- const char *compatible;
-
- while ((node = of_get_next_child(adap_node, node))) {
- struct i2c_board_info info = {};
- const u32 *addr;
- int len;
-
- addr = of_get_property(node, "reg", &len);
- if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) {
- printk(KERN_WARNING "fsl_soc.c: invalid i2c device entry\n");
- continue;
- }
-
- info.irq = irq_of_parse_and_map(node, 0);
- if (info.irq == NO_IRQ)
- info.irq = -1;
-
- compatible = of_get_property(node, "compatible", &len);
- if (!compatible) {
- printk(KERN_WARNING "i2c-mpc.c: invalid entry, missing compatible attribute\n");
- continue;
- }
- strncpy(info.driver_name, compatible, sizeof(info.driver_name));
-
- info.addr = *addr;
-
- i2c_register_board_info(bus_num, &info, 1);
- }
-}
-
-static int __init fsl_i2c_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *i2c_dev;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "i2c", "fsl-i2c")) != NULL;
- i++) {
- struct resource r[2];
- struct fsl_i2c_platform_data i2c_data;
- const unsigned char *flags = NULL;
-
- memset(&r, 0, sizeof(r));
- memset(&i2c_data, 0, sizeof(i2c_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- of_irq_to_resource(np, 0, &r[1]);
-
- i2c_dev = platform_device_register_simple("fsl-i2c", i, r, 2);
- if (IS_ERR(i2c_dev)) {
- ret = PTR_ERR(i2c_dev);
- goto err;
- }
-
- i2c_data.device_flags = 0;
- flags = of_get_property(np, "dfsrr", NULL);
- if (flags)
- i2c_data.device_flags |= FSL_I2C_DEV_SEPARATE_DFSRR;
-
- flags = of_get_property(np, "fsl5200-clocking", NULL);
- if (flags)
- i2c_data.device_flags |= FSL_I2C_DEV_CLOCK_5200;
-
- ret =
- platform_device_add_data(i2c_dev, &i2c_data,
- sizeof(struct
- fsl_i2c_platform_data));
- if (ret)
- goto unreg;
-
- of_register_i2c_devices(np, i);
- }
-
- return 0;
-
-unreg:
- platform_device_unregister(i2c_dev);
-err:
- return ret;
-}
-
-arch_initcall(fsl_i2c_of_init);
-#endif
-
#ifdef CONFIG_PPC_83xx
static int __init mpc83xx_wdt_init(void)
{
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 7c35a8f..6f080d4 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -17,7 +17,7 @@
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <asm/io.h>
#include <linux/fsl_devices.h>
@@ -25,13 +25,13 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
-#define MPC_I2C_ADDR 0x00
+#define DRV_NAME "mpc-i2c"
+
#define MPC_I2C_FDR 0x04
#define MPC_I2C_CR 0x08
#define MPC_I2C_SR 0x0c
#define MPC_I2C_DR 0x10
#define MPC_I2C_DFSRR 0x14
-#define MPC_I2C_REGION 0x20
#define CCR_MEN 0x80
#define CCR_MIEN 0x40
@@ -316,105 +316,167 @@ static struct i2c_adapter mpc_ops = {
.retries = 1
};
-static int fsl_i2c_probe(struct platform_device *pdev)
+struct i2c_driver_device {
+ char *of_device;
+ char *i2c_driver;
+ char *i2c_type;
+};
+
+static void of_register_i2c_devices(struct i2c_adapter *adap, struct device_node *adap_node)
+{
+ struct device_node *node = NULL;
+
+ while ((node = of_get_next_child(adap_node, node))) {
+ struct i2c_board_info info;
+ const u32 *addr;
+ const char *compatible;
+ int len;
+
+ addr = of_get_property(node, "reg", &len);
+ if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) {
+ printk(KERN_ERR "i2c-mpc.c: invalid entry, missing reg attribute\n");
+ continue;
+ }
+
+ info.irq = irq_of_parse_and_map(node, 0);
+ if (info.irq == NO_IRQ)
+ info.irq = -1;
+
+ compatible = of_get_property(node, "compatible", &len);
+ if (!compatible) {
+ printk(KERN_ERR "i2c-mpc.c: invalid entry, missing compatible attribute\n");
+ continue;
+ }
+ strncpy(info.driver_name, compatible, sizeof(info.driver_name));
+ info.type[0] = '\0';
+
+ info.platform_data = NULL;
+ info.addr = *addr;
+
+ if (!i2c_new_device(adap, &info)) {
+ printk(KERN_ERR "i2c-mpc.c: Failed to load driver for %s\n", info.driver_name);
+ continue;
+ }
+ }
+}
+
+static int mpc_i2c_probe(struct of_device *op, const struct of_device_id *match)
{
int result = 0;
struct mpc_i2c *i2c;
- struct fsl_i2c_platform_data *pdata;
- struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
- i2c->irq = platform_get_irq(pdev, 0);
- if (i2c->irq < 0) {
- result = -ENXIO;
- goto fail_get_irq;
- }
- i2c->flags = pdata->device_flags;
- init_waitqueue_head(&i2c->queue);
+ if (of_get_property(op->node, "dfsrr", NULL))
+ i2c->flags |= FSL_I2C_DEV_SEPARATE_DFSRR;
- i2c->base = ioremap((phys_addr_t)r->start, MPC_I2C_REGION);
+ if (of_device_is_compatible(op->node, "mpc5200-i2c"))
+ i2c->flags |= FSL_I2C_DEV_CLOCK_5200;
+ init_waitqueue_head(&i2c->queue);
+
+ i2c->base = of_iomap(op->node, 0);
if (!i2c->base) {
printk(KERN_ERR "i2c-mpc - failed to map controller\n");
result = -ENOMEM;
goto fail_map;
}
- if (i2c->irq != 0)
- if ((result = request_irq(i2c->irq, mpc_i2c_isr,
- IRQF_SHARED, "i2c-mpc", i2c)) < 0) {
- printk(KERN_ERR
- "i2c-mpc - failed to attach interrupt\n");
- goto fail_irq;
- }
+ i2c->irq = irq_of_parse_and_map(op->node, 0);
+ if (i2c->irq == NO_IRQ) {
+ result = -ENXIO;
+ goto fail_irq;
+ }
+
+ result = request_irq(i2c->irq, mpc_i2c_isr, IRQF_SHARED, "i2c-mpc", i2c));
+ if (result < 0) {
+ printk(KERN_ERR "i2c-mpc - failed to attach interrupt\n");
+ goto fail_request;
+ }
mpc_i2c_setclock(i2c);
- platform_set_drvdata(pdev, i2c);
+
+ dev_set_drvdata(&op->dev, i2c);
i2c->adap = mpc_ops;
- i2c->adap.nr = pdev->id;
i2c_set_adapdata(&i2c->adap, i2c);
- i2c->adap.dev.parent = &pdev->dev;
- if ((result = i2c_add_numbered_adapter(&i2c->adap)) < 0) {
+ i2c->adap.dev.parent = &op->dev;
+
+ result = i2c_add_adapter(&i2c->adap);
+ if (result < 0) {
printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
goto fail_add;
}
+ of_register_i2c_devices(&i2c->adap, op->node);
+
return result;
- fail_add:
- if (i2c->irq != 0)
- free_irq(i2c->irq, i2c);
- fail_irq:
+fail_add:
+ free_irq(i2c->irq, i2c);
+fail_request:
+ irq_dispose_mapping(i2c->irq);
+fail_irq:
iounmap(i2c->base);
- fail_map:
- fail_get_irq:
+fail_map:
kfree(i2c);
return result;
};
-static int fsl_i2c_remove(struct platform_device *pdev)
+static int mpc_i2c_remove(struct of_device *op)
{
- struct mpc_i2c *i2c = platform_get_drvdata(pdev);
+ struct mpc_i2c *i2c = dev_get_drvdata(&op->dev);
i2c_del_adapter(&i2c->adap);
- platform_set_drvdata(pdev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
if (i2c->irq != NO_IRQ)
free_irq(i2c->irq, i2c);
+ irq_dispose_mapping(i2c->irq);
iounmap(i2c->base);
kfree(i2c);
return 0;
};
+static struct of_device_id mpc_i2c_of_match[] = {
+ {
+ .compatible = "fsl-i2c",
+ },
+};
+MODULE_DEVICE_TABLE(of, mpc_i2c_of_match);
+
+
/* Structure for a device driver */
-static struct platform_driver fsl_i2c_driver = {
- .probe = fsl_i2c_probe,
- .remove = fsl_i2c_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "fsl-i2c",
+static struct of_platform_driver mpc_i2c_driver = {
+ .match_table = mpc_i2c_of_match,
+ .probe = mpc_i2c_probe,
+ .remove = __devexit_p(mpc_i2c_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
},
};
-static int __init fsl_i2c_init(void)
+static int __init mpc_i2c_init(void)
{
- return platform_driver_register(&fsl_i2c_driver);
+ int rv;
+
+ rv = of_register_platform_driver(&mpc_i2c_driver);
+ if (rv)
+ printk(KERN_ERR DRV_NAME " of_register_platform_driver failed (%i)\n", rv);
+ return rv;
}
-static void __exit fsl_i2c_exit(void)
+static void __exit mpc_i2c_exit(void)
{
- platform_driver_unregister(&fsl_i2c_driver);
+ of_unregister_platform_driver(&mpc_i2c_driver);
}
-module_init(fsl_i2c_init);
-module_exit(fsl_i2c_exit);
+module_init(mpc_i2c_init);
+module_exit(mpc_i2c_exit);
MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
MODULE_DESCRIPTION
^ permalink raw reply related
* [PATCH 0/5] Series to add device tree naming to i2c
From: Jon Smirl @ 2007-12-10 18:33 UTC (permalink / raw)
To: i2c, linuxppc-dev
Respin to split error return fixups out of mpc-i2c to of_platform change
The following series implements standard linux module aliasing for i2c modules
It then converts the mpc i2c driver from being a platform driver to an open
firmware one. I2C device names are picked up from the device tree. Module
aliasing is used to translate from device tree names into to linux kernel
names. Several i2c drivers are updated to use the new aliasing.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Scott Wood @ 2007-12-10 18:37 UTC (permalink / raw)
To: Jon Smirl; +Cc: Jean Delvare, i2c, linuxppc-dev
In-Reply-To: <9e4733910712101006s37829df4w676eea54a98b282d@mail.gmail.com>
Jon Smirl wrote:
> I can't see an easy way to do this. The basic problem is that the i2c
> drivers are assumed to be cross platform.
It'd be a small binding-specific portion, similar to an of_platform stub
on a generic driver. It could probably wait until an actual need
arises, though.
> I would need to add a path through the i2c core for getting a void
> pointer from the bus to the device But then when the device code gets
> this pointer it has no way of knowing what it was.
It'd need to know which binding/name combination it matched against
(similar to how of_platform does it).
> Another way that would work cross platform would be for the module to
> have module parameters for the extra attributes.
Ick. Module parameters are a PITA, and have to be duplicated with
command line parameters if you want to support non-modular builds.
-Scott
^ permalink raw reply
* RE: Xilinx ML310 Linux 2.6 PCI bridge
From: Stephen Neuendorffer @ 2007-12-10 18:47 UTC (permalink / raw)
To: Jean-Samuel Chenard; +Cc: linuxppc-embedded
In-Reply-To: <169c03cb0712100833m36842bf7j95aab235f67b5250@mail.gmail.com>
=20
> That's where things get complicated... In our lab, we get expensive
> hardware such as the BEE2 via CMC Microsystems (a Canadian granting
> agency). However, getting funds locally to buy other equipment is a
> relatively long and administratively complex process...
Yeah, I understand how such things go... :)
> I find it strange that Xilinx would not invest in supporting Linux 2.6
> for an important feature such as a PCI bridge (not specifically for
> the ML-310, but in general) when this can bring many interesting
> benefits to an embedded platform. Is this something that is "in
> progress" at Xilinx? You seem to imply that most of the difficulty in
> getting the PCI core working is in meeting the timing requirements
> (not with the SW drivers), so I might be missing driver files when I
> generate the BSP code for Linux 2.6.
The PCI core needs a separate driver for generating the right
constraints. In EDK 9.2 there is some linux 2.6 support for this, but
the code won't work with any of the new kernels. I've pulled the
changes into my internal git tree, but for reasons that I haven't been
able to ascertain, there are hardcoded xparameter-based interrupt
defines in the super I/O bridge controller.
In any event, I'll send you the patches directly and you can try getting
it to work, although you'll likely need to carefully write the
xparameters by hand. If you can get away without networking, I'd do
that, though. :)
Steve=20
^ permalink raw reply
* Re: [PATCH 2/3] arch/ : Platform changes for UCC TDM driver for MPC8323ERDB.Also includes related QE changes.
From: Timur Tabi @ 2007-12-10 18:48 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812; +Cc: linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0712101734490.29623@linux121>
Poonam_Aggrwal-b10812 wrote:
> + qe = of_find_node_by_type(NULL, "qe");
> + if (qe) {
> + unsigned int size;
> + prop = of_get_property
> + (qe, "brg-frequency", &size);
> + of_node_put(qe);
> + of_node_put(brg);
> + return *prop;
> + }
Only very recent versions of U-Boot set the brg-frequency property, so you need
to check for situations where "*prop" is 0. If it is, then you need to take the
QE's bus-frequency property and divide it by two. See my ucc_uart driver for an
example.
And PowerPC-specific patches should not be cross-posted to linux-kernel.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Jon Smirl @ 2007-12-10 18:52 UTC (permalink / raw)
To: Scott Wood; +Cc: Jean Delvare, i2c, linuxppc-dev
In-Reply-To: <475D8756.2000906@freescale.com>
On 12/10/07, Scott Wood <scottwood@freescale.com> wrote:
> Jon Smirl wrote:
> > I can't see an easy way to do this. The basic problem is that the i2c
> > drivers are assumed to be cross platform.
>
> It'd be a small binding-specific portion, similar to an of_platform stub
> on a generic driver. It could probably wait until an actual need
> arises, though.
>
> > I would need to add a path through the i2c core for getting a void
> > pointer from the bus to the device But then when the device code gets
> > this pointer it has no way of knowing what it was.
>
> It'd need to know which binding/name combination it matched against
> (similar to how of_platform does it).
>
> > Another way that would work cross platform would be for the module to
> > have module parameters for the extra attributes.
>
> Ick. Module parameters are a PITA, and have to be duplicated with
> command line parameters if you want to support non-modular builds.
You don't have to duplicate module parameters in non-modular builds
(you needed to a couple of years ago). Even if the module is linked in
you can still set the parameters from the kernel command line without
doing anything special. Just use module.param = value.
>
> -Scott
>
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [PATCH] Fake NUMA emulation for PowerPC (Take 2)
From: Balbir Singh @ 2007-12-10 19:36 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Andrew Morton, LKML
In-Reply-To: <20071207223714.11448.91386.sendpatchset@balbir-laptop>
Balbir Singh wrote:
> Changelog
>
> 1. Get rid of the constant 5 (based on comments from
> Geert.Uytterhoeven@sonycom.com)
> 2. Implement suggestions from Olof Johannson
> 3. Check if cmdline is NULL in fake_numa_create_new_node()
>
> Tested with additional parameters from Olof
>
> numa=debug,fake=
> numa=foo,fake=bar
>
>
> Here's a dumb simple implementation of fake NUMA nodes for PowerPC. Fake
> NUMA nodes can be specified using the following command line option
>
> numa=fake=<node range>
>
> node range is of the format <range1>,<range2>,...<rangeN>
>
> Each of the rangeX parameters is passed using memparse(). I find the patch
> useful for fake NUMA emulation on my simple PowerPC machine. I've tested it
> on a non-numa box with the following arguments
>
> numa=fake=1G
> numa=fake=1G,2G
> name=fake=1G,512M,2G
> numa=fake=1500M,2800M mem=3500M
> numa=fake=1G mem=512M
> numa=fake=1G mem=1G
>
> This patch applies on top of 2.6.24-rc4.
>
> All though I've tried my best to handle some of the architecture specific
> details of PowerPC, I might have overlooked something obvious, like the usage
> of an API or some architecture tweaks. The patch depends on CONFIG_NUMA and
> I decided against creating a separate config option for fake NUMA to keep
> the code simple.
>
> Comments are as always welcome!
>
> Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
> ---
>
> arch/powerpc/mm/numa.c | 59 ++++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 54 insertions(+), 5 deletions(-)
>
> diff -puN arch/powerpc/mm/numa.c~ppc-fake-numa-easy arch/powerpc/mm/numa.c
> --- linux-2.6.24-rc4-mm1/arch/powerpc/mm/numa.c~ppc-fake-numa-easy 2007-12-07 21:25:55.000000000 +0530
> +++ linux-2.6.24-rc4-mm1-balbir/arch/powerpc/mm/numa.c 2007-12-08 03:19:46.000000000 +0530
> @@ -24,6 +24,8 @@
>
> static int numa_enabled = 1;
>
> +static char *cmdline __initdata;
> +
> static int numa_debug;
> #define dbg(args...) if (numa_debug) { printk(KERN_INFO args); }
>
> @@ -39,6 +41,43 @@ static bootmem_data_t __initdata plat_no
> static int min_common_depth;
> static int n_mem_addr_cells, n_mem_size_cells;
>
> +static int __cpuinit fake_numa_create_new_node(unsigned long end_pfn,
> + unsigned int *nid)
> +{
> + unsigned long long mem;
> + char *p = cmdline;
> + static unsigned int fake_nid = 0;
> + static unsigned long long curr_boundary = 0;
> +
> + *nid = fake_nid;
> + if (!p)
> + return 0;
> +
> + mem = memparse(p, &p);
> + if (!mem)
> + return 0;
> +
> + if (mem < curr_boundary)
> + return 0;
> +
> + curr_boundary = mem;
> +
> + if ((end_pfn << PAGE_SHIFT) > mem) {
> + /*
> + * Skip commas and spaces
> + */
> + while (*p == ',' || *p == ' ' || *p == '\t')
> + p++;
> +
> + cmdline = p;
> + fake_nid++;
> + *nid = fake_nid;
> + dbg("created new fake_node with id %d\n", fake_nid);
> + return 1;
> + }
> + return 0;
> +}
> +
> static void __cpuinit map_cpu_to_node(int cpu, int node)
> {
> numa_cpu_lookup_table[cpu] = node;
> @@ -344,12 +383,14 @@ static void __init parse_drconf_memory(s
> if (nid == 0xffff || nid >= MAX_NUMNODES)
> nid = default_nid;
> }
> - node_set_online(nid);
>
> size = numa_enforce_memory_limit(start, lmb_size);
> if (!size)
> continue;
>
> + fake_numa_create_new_node(((start + size) >> PAGE_SHIFT), &nid);
> + node_set_online(nid);
> +
> add_active_range(nid, start >> PAGE_SHIFT,
> (start >> PAGE_SHIFT) + (size >> PAGE_SHIFT));
> }
> @@ -429,7 +470,6 @@ new_range:
> nid = of_node_to_nid_single(memory);
> if (nid < 0)
> nid = default_nid;
> - node_set_online(nid);
>
> if (!(size = numa_enforce_memory_limit(start, size))) {
> if (--ranges)
> @@ -438,6 +478,9 @@ new_range:
> continue;
> }
>
> + fake_numa_create_new_node(((start + size) >> PAGE_SHIFT), &nid);
> + node_set_online(nid);
> +
> add_active_range(nid, start >> PAGE_SHIFT,
> (start >> PAGE_SHIFT) + (size >> PAGE_SHIFT));
>
> @@ -461,7 +504,7 @@ static void __init setup_nonnuma(void)
> unsigned long top_of_ram = lmb_end_of_DRAM();
> unsigned long total_ram = lmb_phys_mem_size();
> unsigned long start_pfn, end_pfn;
> - unsigned int i;
> + unsigned int i, nid = 0;
>
> printk(KERN_DEBUG "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
> top_of_ram, total_ram);
> @@ -471,9 +514,11 @@ static void __init setup_nonnuma(void)
> for (i = 0; i < lmb.memory.cnt; ++i) {
> start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
> end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
> - add_active_range(0, start_pfn, end_pfn);
> +
> + fake_numa_create_new_node(end_pfn, &nid);
> + add_active_range(nid, start_pfn, end_pfn);
> + node_set_online(nid);
> }
> - node_set_online(0);
> }
>
> void __init dump_numa_cpu_topology(void)
> @@ -702,6 +747,10 @@ static int __init early_numa(char *p)
> if (strstr(p, "debug"))
> numa_debug = 1;
>
> + p = strstr(p, "fake=");
> + if (p)
> + cmdline = p + strlen("fake=");
> +
> return 0;
> }
> early_param("numa", early_numa);
> _
>
If there are no other major objections, could we get this infrastructure
into -mm?
--
Warm Regards,
Balbir Singh
Linux Technology Center
IBM, ISTL
^ permalink raw reply
* [PATCH] [POWERPC][RFC] MPC8360E-RDK: Device tree and board file
From: Anton Vorontsov @ 2007-12-10 20:29 UTC (permalink / raw)
To: linuxppc-dev
This is new board made by Freescale Semiconductor Inc. and
Logic Product Development.
Currently supported:
1. UEC1,2 (UEC2 doesn't work, but I'm sure this is firmware issue)
2. I2C
3. SPI
4. NS16550 serial
Not supported so far:
1. StMICRO NAND512W3A2BN6E, 512 Mbit
2. UEC3,4
3. QE SCCs (slow UCCs)
4. PCI
5. ADC AD7843
6. FHCI USB
7. Graphics controller, Fujitsu MB86277
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
Hi all,
That patch is early RFC:
I tend to submit patches just as they are mature enough, thus not bomb
the list with long queues or huge patches. After I'll fix all upcoming
issues with that basic support, it would be great if someone will
merge it, thus I can start do incremental patches supporting this or
that.
Below is MPC8360E-RDK basic support. I'm following latest fashion,
so dts is v1. ;-)
Thanks,
p.s. not sending defconfig yet.
arch/powerpc/boot/dts/mpc836x_rdk.dts | 232 +++++++++++++++++++++++++++++
arch/powerpc/platforms/83xx/Kconfig | 10 +-
arch/powerpc/platforms/83xx/Makefile | 1 +
arch/powerpc/platforms/83xx/mpc836x_rdk.c | 109 ++++++++++++++
4 files changed, 351 insertions(+), 1 deletions(-)
create mode 100644 arch/powerpc/boot/dts/mpc836x_rdk.dts
create mode 100644 arch/powerpc/platforms/83xx/mpc836x_rdk.c
diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts
new file mode 100644
index 0000000..a3b37e8
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts
@@ -0,0 +1,232 @@
+/*
+ * MPC8360E RDK Device Tree Source
+ *
+ * Copyright 2006 Freescale Semiconductor Inc.
+ * Copyright 2007 MontaVista Software, Inc.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/dts-v1/;
+
+/ {
+ model = "MPC8360RDK";
+ compatible = "MPC8360ERDK", "MPC836xRDK", "MPC83xxRDK";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,8360@0 {
+ device_type = "cpu";
+ reg = <0>;
+ d-cache-line-size = <32>;
+ i-cache-line-size = <32>;
+ d-cache-size = <32768>;
+ i-cache-size = <32768>;
+ timebase-frequency = <0>; /* filled by u-boot */
+ bus-frequency = <0>; /* filled by u-boot */
+ clock-frequency = <0>; /* filled by u-boot */
+ };
+ };
+
+ soc8360@e0000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ ranges = <0 0xe0000000 0x00100000>;
+ reg = <0xe0000000 0x00000200>;
+ bus-frequency = <0>; /* filled by u-boot */
+
+ wdt@200 {
+ device_type = "watchdog";
+ compatible = "mpc83xx_wdt";
+ reg = <0x200 0x100>;
+ };
+
+ i2c@3000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ device_type = "i2c";
+ compatible = "fsl-i2c";
+ reg = <0x3000 0x100>;
+ interrupts = <14 8>;
+ interrupt-parent = <&ipic>;
+ dfsrr;
+ };
+
+ i2c@3100 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ device_type = "i2c";
+ compatible = "fsl-i2c";
+ reg = <0x3100 0x100>;
+ interrupts = <16 8>;
+ interrupt-parent = <&ipic>;
+ dfsrr;
+ };
+
+ serial@4500 {
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4500 0x100>;
+ clock-frequency = <0>;
+ interrupts = <9 8>;
+ interrupt-parent = <&ipic>;
+ };
+
+ serial@4600 {
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4600 0x100>;
+ clock-frequency = <0>;
+ interrupts = <10 8>;
+ interrupt-parent = <&ipic>;
+ };
+
+ crypto@30000 {
+ device_type = "crypto";
+ model = "SEC2";
+ compatible = "talitos";
+ reg = <0x30000 0x10000>;
+ interrupts = <11 8>;
+ interrupt-parent = <&ipic>;
+ num-channels = <4>;
+ channel-fifo-len = <24>;
+ exec-units-mask = <0x0000007e>;
+ /*
+ * desc mask is for rev1.x, we need runtime fixup
+ * for >=2.x
+ */
+ descriptor-types-mask = <0x01010ebf>;
+ };
+
+ ipic: pic@700 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x700 0x100>;
+ device_type = "ipic";
+ };
+
+ par_io@1400 {
+ reg = <0x1400 0x100>;
+ num-ports = <7>;
+ };
+ };
+
+ qe@e0100000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "qe";
+ model = "QE";
+ ranges = <0 0xe0100000 0x00100000>;
+ reg = <0xe0100000 0x480>;
+ brg-frequency = <0>; /* filled by u-boot */
+ bus-frequency = <0>; /* filled by u-boot */
+
+ muram@10000 {
+ device_type = "muram";
+ ranges = <0 0x00010000 0x0000c000>;
+
+ data-only@0 {
+ reg = <0 0xc000>;
+ };
+ };
+
+ spi@4c0 {
+ device_type = "spi";
+ compatible = "fsl_spi";
+ reg = <0x4c0 0x40>;
+ interrupts = <2>;
+ interrupt-parent = <&qeic>;
+ mode = "cpu";
+ };
+
+ spi@500 {
+ device_type = "spi";
+ compatible = "fsl_spi";
+ reg = <0x500 0x40>;
+ interrupts = <1>;
+ interrupt-parent = <&qeic>;
+ mode = "cpu";
+ };
+
+ usb@6c0 {
+ device_type = "usb";
+ compatible = "qe_udc";
+ reg = <0x6c0 0x40 0x8b00 0x100>;
+ interrupts = <11>;
+ interrupt-parent = <&qeic>;
+ mode = "slave";
+ };
+
+ ucc@2000 {
+ device_type = "network";
+ compatible = "ucc_geth";
+ model = "UCC";
+ device-id = <1>;
+ reg = <0x2000 0x200>;
+ interrupts = <32>;
+ interrupt-parent = <&qeic>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ rx-clock = <0>;
+ tx-clock = <25>;
+ phy-handle = <&phy2>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ ucc@3000 {
+ device_type = "network";
+ compatible = "ucc_geth";
+ model = "UCC";
+ device-id = <2>;
+ reg = <0x3000 0x200>;
+ interrupts = <33>;
+ interrupt-parent = <&qeic>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ rx-clock = <0>;
+ tx-clock = <20>;
+ phy-handle = <&phy4>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ mdio@2120 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x2120 0x18>;
+ device_type = "mdio";
+ compatible = "ucc_geth_phy";
+
+ phy2: ethernet-phy@02 {
+ interrupt-parent = <&ipic>;
+ /*interrupts = <17 8>;*/
+ reg = <2>;
+ device_type = "ethernet-phy";
+ };
+ phy4: ethernet-phy@04 {
+ interrupt-parent = <&ipic>;
+ /*interrupts = <18 8>;*/
+ reg = <4>;
+ device_type = "ethernet-phy";
+ };
+ };
+
+ qeic: qeic@80 {
+ interrupt-controller;
+ device_type = "qeic";
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ reg = <0x80 0x80>;
+ big-endian;
+ interrupts = <32 8 33 8>;
+ interrupt-parent = <&ipic>;
+ };
+ };
+};
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index ec305f1..98f6358 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -50,6 +50,14 @@ config MPC836x_MDS
help
This option enables support for the MPC836x MDS Processor Board.
+config MPC836x_RDK
+ bool "Freescale/Logic MPC836x RDK"
+ select DEFAULT_UIMAGE
+ select QUICC_ENGINE
+ help
+ This option enables support for the MPC836x RDK Processor Board,
+ also known as ZOOM PowerQUICC Kit.
+
endchoice
config PPC_MPC831x
@@ -74,4 +82,4 @@ config PPC_MPC836x
bool
select PPC_UDBG_16550
select PPC_INDIRECT_PCI
- default y if MPC836x_MDS
+ default y if MPC836x_MDS || MPC836x_RDK
diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile
index 5a98f88..24dcd75 100644
--- a/arch/powerpc/platforms/83xx/Makefile
+++ b/arch/powerpc/platforms/83xx/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o
obj-$(CONFIG_MPC834x_ITX) += mpc834x_itx.o
obj-$(CONFIG_MPC836x_MDS) += mpc836x_mds.o
obj-$(CONFIG_MPC832x_MDS) += mpc832x_mds.o
+obj-$(CONFIG_MPC836x_RDK) += mpc836x_rdk.o
diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
new file mode 100644
index 0000000..be9e2fd
--- /dev/null
+++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
@@ -0,0 +1,109 @@
+/*
+ * MPC8360E-RDK board file.
+ *
+ * Copyright (c) 2006 Freescale Semicondutor, Inc.
+ * Copyright (c) 2007 MontaVista Software, Inc.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <asm/time.h>
+#include <asm/ipic.h>
+#include <asm/udbg.h>
+#include <asm/io.h>
+#include <asm/qe.h>
+#include <asm/qe_ic.h>
+#include <sysdev/fsl_soc.h>
+
+#include "mpc83xx.h"
+
+static struct of_device_id mpc836x_rdk_ids[] = {
+ { .type = "soc", },
+ { .compatible = "soc", },
+ { .type = "qe", },
+ {},
+};
+
+static int __init mpc836x_rdk_declare_of_platform_devices(void)
+{
+ if (!machine_is(mpc836x_rdk))
+ return 0;
+
+ of_platform_bus_probe(NULL, mpc836x_rdk_ids, NULL);
+
+ return 0;
+}
+device_initcall(mpc836x_rdk_declare_of_platform_devices);
+
+static void __init mpc836x_rdk_setup_arch(void)
+{
+ struct device_node *np;
+
+ if (ppc_md.progress)
+ ppc_md.progress("mpc836x_rdk_setup_arch()", 0);
+
+#ifdef CONFIG_QUICC_ENGINE
+ qe_reset();
+
+ np = of_find_node_by_name(NULL, "par_io");
+ if (np)
+ par_io_init(np);
+ else
+ pr_warning("QE PIO not initialized!\n");
+#endif
+}
+
+static void __init mpc836x_rdk_init_IRQ(void)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_type(NULL, "ipic");
+ if (!np)
+ return;
+
+ ipic_init(np, 0);
+
+ /*
+ * Initialize the default interrupt mapping priorities,
+ * in case the boot rom changed something on us.
+ */
+ ipic_set_default_priority();
+ of_node_put(np);
+
+#ifdef CONFIG_QUICC_ENGINE
+ np = of_find_node_by_type(NULL, "qeic");
+ if (!np)
+ return;
+
+ qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic);
+ of_node_put(np);
+#endif
+}
+
+/*
+ * Called very early, MMU is off, device-tree isn't unflattened
+ */
+static int __init mpc836x_rdk_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ return of_flat_dt_is_compatible(root, "MPC836xRDK");
+}
+
+define_machine(mpc836x_rdk) {
+ .name = "MPC836x RDK",
+ .probe = mpc836x_rdk_probe,
+ .setup_arch = mpc836x_rdk_setup_arch,
+ .init_IRQ = mpc836x_rdk_init_IRQ,
+ .get_irq = ipic_get_irq,
+ .restart = mpc83xx_restart,
+ .time_init = mpc83xx_time_init,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = udbg_progress,
+};
--
1.5.2.2
^ permalink raw reply related
* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Benjamin Herrenschmidt @ 2007-12-10 20:32 UTC (permalink / raw)
To: Scott Wood; +Cc: Jean Delvare, linuxppc-dev, i2c
In-Reply-To: <20071210164255.GA4497@loki.buserror.net>
On Mon, 2007-12-10 at 10:42 -0600, Scott Wood wrote:
> On Mon, Dec 10, 2007 at 08:38:46AM +1100, Benjamin Herrenschmidt wrote:
> > The more I think about it, the more I tend to agree that tagging isn't
> > necessary and you are right. We should just match the name against the
> > "compatible" property of the OF nodes (which mean we need to support
> > multiple matches though since "compatible" is a list of strings).
>
> It may not be strictly necessary, but I think it's a good idea not just for
> safety reasons, but as an indication to the driver what additional
> information it has access to. We could put a match data pointer in the i2c
> device, and have it be a valid node pointer if the match was an OF one (and
> a device-specific struct for a straight platform device, etc). This could
> be useful if a device needs to have more properties than standard
> address/type/interrupt for some reason.
You don't need that much... On ppc64, all devices have an optional
device node pointer in struct device via the archdata, an we should do
that on ppc32 too (and will soon in order to merge some of the PCI & DMA
stuff anyway).
Ben.
^ permalink raw reply
* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Benjamin Herrenschmidt @ 2007-12-10 20:35 UTC (permalink / raw)
To: Jon Smirl; +Cc: Jean Delvare, i2c, linuxppc-dev
In-Reply-To: <9e4733910712101006s37829df4w676eea54a98b282d@mail.gmail.com>
On Mon, 2007-12-10 at 13:06 -0500, Jon Smirl wrote:
>
> I can't see an easy way to do this. The basic problem is that the i2c
> drivers are assumed to be cross platform. I would need to add a path
> through the i2c core for getting a void pointer from the bus to the
> device But then when the device code gets this pointer it has no way
> of knowing what it was. Assuming the void is a pointer to an of_node
> would make the driver for the i2c device platform specific.
As I said, there's an arch data structure that can be added to -any-
struct device and that we use for, among others, an optional device tree
node pointer on ppc64. We should do that on ppc32 too (and will soon for
other reasons). In this case, the i2c core can be modified on powerpc so
that when it instanciate an i2c device from the device-tree, it fills
that field.
Some device drivers can have powerpc specific code that make good use of
that for things like calibration infos etc...
Ben.
^ permalink raw reply
* Re: [PATCH 4/25] powerpc: Reworking machine check handling and Fix 440/440A
From: Benjamin Herrenschmidt @ 2007-12-10 20:33 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20071210115926.20278214@weaponx>
On Mon, 2007-12-10 at 11:59 -0600, Josh Boyer wrote:
>
> This breaks ARCH=ppc builds. Unfortunately, that tree shares the
> cputable.[ch] files, but has it's own traps.c. Which means you get
> lots of nice undefined references like below for example:
>
> arch/powerpc/kernel/built-in.o:(.init.data+0x44): undefined reference
> to `machine_check_4xx'
> arch/powerpc/kernel/built-in.o:(.init.data+0x8c): undefined reference
> to `machine_check_4xx'
> arch/powerpc/kernel/built-in.o:(.init.data+0xd4): undefined reference
> to `machine_check_4xx'
>
> Because the cputable entries for the processors are setting
> the .machine_check function and it's never built.
>
> I'm not sure which would be easier, making arch/ppc use traps.c from
> arch/powerpc, or adding similar functionality there.
Split cputable.c ? I hate arch/ppc sharing files ... Or I could port the
changes to arch/ppc. I don't want to use the same traps.c file. Sharing
file is just a pain every time we do major changes.
Ben.
^ permalink raw reply
* [PATCH RFC 0/7] "NAND on UPM" and related patches
From: Anton Vorontsov @ 2007-12-10 20:47 UTC (permalink / raw)
To: linuxppc-dev
Hi all,
Here are patches to support NAND on UPM. That driver is generic for
all processors with FSL UPMs. And because of that, few more patches are
needed -- GPIO API and generic FSL UPM functions.
This is early RFC, all patches are in single thread, so everyone could
make up overall picture of what is going on. I'll split the thread by
topics after that RFC.
Ok, the patches and what they are for:
1,2,3,4. GPIO API:
------------------
Usually NAND chips exports RNB (Ready-Not-Busy) pin, so drivers
could read it and get a hint when chip is ready.
Often, WP (write protect) pin is also connected to GPIO. So, GPIO API
is mandatory for generic drivers.
OF device tree GPIOs bindings are similar to IRQs:
node {
gpios = <bank pin bank pin bank pin>;
gpio-parent = <&par_io_controller>;
};
"bank pin" scheme is controller specific, so controllers that want
to implement flat mappings or any other could do so.
So far I implemented GPIO API for QE and CPM2 chips.
QE GPIO API tested to work with FSL UPM NAND driver and MPC8360E-RDK
(STMicro NAND512W3A2BN6E).
CPM2 GPIO API tested to work with MPC8555E+Samsung HY27UF081G6 (LP).
GPIO API is described in Documentation/gpio.txt, and these
patches are tend to support most of it.
As an additional bonus, PowerPC now gets access to few pleasing
drivers:
- drivers/leds/leds-gpio.c (we could use it to play with
on-board LEDs);
- drivers/i2c/busses/i2c-gpio.c (generic I2C bit-banging driver);
- drivers/input/keyboard/gpio_keys.c - gpio keys (requires
gpio_to_irq, so far not implemented);
- Could be more (I named the ones I knew about).
Also, in the upcoming kernels, there will be GPIOLIB[1] addition to
the generic GPIO API, to support off-chip GPIO expanders (like MFDs
on I2C/LBC).
[1] http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.24-rc4/2.6.24-rc4-mm1/broken-out/generic-gpio-gpio_chip-support.patch
But so far we support on-chip GPIOs only.
5 - FSL UPM infrastructure:
---------------------------
UPM address register is shared among UPMs, so we have to do
proper locking. On the other hand, if we know that specific
board using only one UPM we could bypass locking, and gain some
performance win.
6. FSL UPM NAND driver:
-----------------------
It's using FSL UPM functions and GPIO API. It does not implement
device tree partitions parsing. That issue is completely other
matter, that is, I have to factor out parsing functions from
physmap_of.c. This desires separate patch on top of the whole
series. If anyone currently working on this, let me know.
7. Example of usage.
--------------------
MPC8360E-RDK as an example.
I would appreciate any comments and suggestions, thanks!
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH 4/25] powerpc: Reworking machine check handling and Fix 440/440A
From: Josh Boyer @ 2007-12-10 20:44 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev
In-Reply-To: <1197318815.8692.7.camel@pasglop>
On Tue, 11 Dec 2007 07:33:35 +1100
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
>
> On Mon, 2007-12-10 at 11:59 -0600, Josh Boyer wrote:
> >
> > This breaks ARCH=ppc builds. Unfortunately, that tree shares the
> > cputable.[ch] files, but has it's own traps.c. Which means you get
> > lots of nice undefined references like below for example:
> >
> > arch/powerpc/kernel/built-in.o:(.init.data+0x44): undefined reference
> > to `machine_check_4xx'
> > arch/powerpc/kernel/built-in.o:(.init.data+0x8c): undefined reference
> > to `machine_check_4xx'
> > arch/powerpc/kernel/built-in.o:(.init.data+0xd4): undefined reference
> > to `machine_check_4xx'
> >
> > Because the cputable entries for the processors are setting
> > the .machine_check function and it's never built.
> >
> > I'm not sure which would be easier, making arch/ppc use traps.c from
> > arch/powerpc, or adding similar functionality there.
>
> Split cputable.c ? I hate arch/ppc sharing files ... Or I could port the
> changes to arch/ppc. I don't want to use the same traps.c file. Sharing
> file is just a pain every time we do major changes.
Splitting cputable.c at this point would be rather annoying, given the
number of changes we've made to arch/ppc recently to accommodate for it
being shared. Porting the changes to arch/ppc sounds like the most
reasonable path.
I can't wait for arch/ppc to die.
josh
^ permalink raw reply
* [PATCH RFC 1/7] [POWERPC] Implement GPIO API embryo
From: Anton Vorontsov @ 2007-12-10 20:48 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>
This patch implements GPIO API as described in Documentation/gpio.txt.
Two calls unimplemented though: irq_to_gpio and gpio_to_irq.
I'm also providing OF helpers.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
arch/powerpc/Kconfig | 4 +++
arch/powerpc/kernel/prom_parse.c | 50 +++++++++++++++++++++++++++++++++++++
include/asm-powerpc/gpio.h | 51 ++++++++++++++++++++++++++++++++++++++
include/asm-powerpc/prom.h | 23 +++++++++++++++++
4 files changed, 128 insertions(+), 0 deletions(-)
create mode 100644 include/asm-powerpc/gpio.h
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 232c298..596982f 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -73,6 +73,10 @@ config GENERIC_FIND_NEXT_BIT
bool
default y
+config GENERIC_GPIO
+ bool
+ default n
+
config ARCH_NO_VIRT_TO_BUS
def_bool PPC64
diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c
index b5c96af..5228fe6 100644
--- a/arch/powerpc/kernel/prom_parse.c
+++ b/arch/powerpc/kernel/prom_parse.c
@@ -7,6 +7,7 @@
#include <linux/ioport.h>
#include <linux/etherdevice.h>
#include <asm/prom.h>
+#include <asm/gpio.h>
#include <asm/pci-bridge.h>
#ifdef DEBUG
@@ -1067,3 +1068,52 @@ void __iomem *of_iomap(struct device_node *np, int index)
return ioremap(res.start, 1 + res.end - res.start);
}
EXPORT_SYMBOL(of_iomap);
+
+int __of_parse_gpio_bank_pin(struct device_node *np, int index,
+ int bank_width, int max_bank)
+{
+ int bank;
+ int pin;
+ const u32 *gpios;
+ int len;
+
+ gpios = of_get_property(np, "gpios", &len);
+ len /= sizeof(u32);
+
+ if (len < 2 || len % 2 || index > len / 2 - 1)
+ return -EINVAL;
+
+ bank = gpios[index * 2];
+ pin = gpios[index * 2 + 1];
+
+ if (bank >= max_bank || pin >= bank_width)
+ return -EINVAL;
+
+ return bank * bank_width + pin;
+}
+EXPORT_SYMBOL_GPL(__of_parse_gpio_bank_pin);
+
+int of_get_gpio(struct device_node *np, int index)
+{
+ int ret;
+ const phandle *gc_ph;
+ struct device_node *gc;
+ struct of_gpio_chip *of_gpio_chip;
+
+ gc_ph = of_get_property(np, "gpio-parent", NULL);
+ if (!gc_ph)
+ return -EINVAL;
+
+ gc = of_find_node_by_phandle(*gc_ph);
+ if (!gc || !gc->data)
+ return -EINVAL;
+
+ of_gpio_chip = gc->data;
+
+ ret = of_gpio_chip->xlate(np, index);
+ if (ret < 0)
+ of_node_put(gc);
+
+ return ret;
+}
+EXPORT_SYMBOL(of_get_gpio);
diff --git a/include/asm-powerpc/gpio.h b/include/asm-powerpc/gpio.h
new file mode 100644
index 0000000..f7513ff
--- /dev/null
+++ b/include/asm-powerpc/gpio.h
@@ -0,0 +1,51 @@
+/*
+ * Generic GPIO API implementation for PowerPC.
+ *
+ * Copyright (c) 2007 MontaVista Software, Inc.
+ * Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __ASM_POWERPC_GPIO_H
+#define __ASM_POWERPC_GPIO_H
+
+#ifdef CONFIG_GENERIC_GPIO
+
+extern int gpio_request(unsigned int gpio, const char *label);
+static inline void gpio_free(unsigned int gpio) {}
+
+extern int gpio_direction_input(unsigned int gpio);
+extern int gpio_direction_output(unsigned int gpio, int value);
+
+extern int gpio_get_value(unsigned int gpio);
+extern int gpio_set_value(unsigned int gpio, int value);
+
+/*
+ * Not implemented, yet.
+ */
+static inline int gpio_to_irq(unsigned int gpio)
+{
+ return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ return -ENOSYS;
+}
+
+/*
+ * OF specific gpio_chip handler.
+ */
+struct of_gpio_chip {
+ int (*xlate)(struct device_node *np, int index);
+};
+
+#endif /* CONFIG_GENERIC_GPIO */
+
+#include <asm-generic/gpio.h>
+
+#endif /* __ASM_POWERPC_GPIO_H */
diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h
index 925e2d3..019af61 100644
--- a/include/asm-powerpc/prom.h
+++ b/include/asm-powerpc/prom.h
@@ -326,6 +326,29 @@ extern int of_irq_to_resource(struct device_node *dev, int index,
*/
extern void __iomem *of_iomap(struct device_node *device, int index);
+/**
+ * __of_parse_gpio_bank_pin - Helper function to translate "bank pin" GPIOs
+ * @np: device node to get GPIO from
+ * @index: index of the GPIO
+ * @bank_width: pins per bank
+ * @max_bank: maximum value to represent a bank
+ *
+ * Returns GPIO number to use with Linux generic GPIO api, or
+ * one of the errno value on the error condition.
+ */
+extern int __of_parse_gpio_bank_pin(struct device_node *np, int index,
+ int bank_width, int max_bank);
+
+/**
+ * of_get_gpio - Translate GPIO number given in the device tree
+ * @np: device node to get GPIO from
+ * @index: index of the GPIO
+ *
+ * Returns GPIO number to use with Linux generic GPIO API, or
+ * one of the errno value on the error condition.
+ */
+extern int of_get_gpio(struct device_node *np, int index);
+
/*
* NB: This is here while we transition from using asm/prom.h
* to linux/of.h
--
1.5.2.2
^ permalink raw reply related
* [PATCH RFC 2/7] [POWERPC] QE: implement GPIO API
From: Anton Vorontsov @ 2007-12-10 20:48 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
arch/powerpc/platforms/Kconfig | 1 +
arch/powerpc/sysdev/qe_lib/qe_io.c | 93 +++++++++++++++++++++++++++++++++++-
2 files changed, 92 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index ea22cad..3d9ff27 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -265,6 +265,7 @@ config TAU_AVERAGE
config QUICC_ENGINE
bool
select PPC_LIB_RHEAP
+ select GENERIC_GPIO
help
The QUICC Engine (QE) is a new generation of communications
coprocessors on Freescale embedded CPUs (akin to CPM in older chips).
diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index e53ea4d..3c9cf65 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -23,6 +23,7 @@
#include <asm/io.h>
#include <asm/prom.h>
+#include <asm/gpio.h>
#include <sysdev/fsl_soc.h>
#undef DEBUG
@@ -43,12 +44,23 @@ struct port_regs {
static struct port_regs *par_io = NULL;
static int num_par_io_ports = 0;
+static spinlock_t *qe_pio_locks;
+
+static int par_io_xlate(struct device_node *np, int index)
+{
+ return __of_parse_gpio_bank_pin(np, index, 32, num_par_io_ports);
+}
+
+static struct of_gpio_chip of_gpio_chip = {
+ .xlate = par_io_xlate,
+};
int par_io_init(struct device_node *np)
{
struct resource res;
int ret;
const u32 *num_ports;
+ int i;
/* Map Parallel I/O ports registers */
ret = of_address_to_resource(np, 0, &res);
@@ -60,6 +72,18 @@ int par_io_init(struct device_node *np)
if (num_ports)
num_par_io_ports = *num_ports;
+ qe_pio_locks = kzalloc(sizeof(*qe_pio_locks) * num_par_io_ports,
+ GFP_KERNEL);
+ if (!qe_pio_locks) {
+ iounmap(par_io);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_par_io_ports; i++)
+ spin_lock_init(&qe_pio_locks[i]);
+
+ np->data = &of_gpio_chip;
+
return 0;
}
@@ -67,9 +91,12 @@ int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
int assignment, int has_irq)
{
u32 pin_mask1bit, pin_mask2bits, new_mask2bits, tmp_val;
+ unsigned long flags;
- if (!par_io)
- return -1;
+ if (!par_io || port > num_par_io_ports)
+ return -EINVAL;
+
+ spin_lock_irqsave(&qe_pio_locks[port], flags);
/* calculate pin location for single and 2 bits information */
pin_mask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1)));
@@ -126,6 +153,8 @@ int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
out_be32(&par_io[port].cppar1, new_mask2bits | tmp_val);
}
+ spin_unlock_irqrestore(&qe_pio_locks[port], flags);
+
return 0;
}
EXPORT_SYMBOL(par_io_config_pin);
@@ -133,6 +162,7 @@ EXPORT_SYMBOL(par_io_config_pin);
int par_io_data_set(u8 port, u8 pin, u8 val)
{
u32 pin_mask, tmp_val;
+ unsigned long flags;
if (port >= num_par_io_ports)
return -EINVAL;
@@ -141,6 +171,8 @@ int par_io_data_set(u8 port, u8 pin, u8 val)
/* calculate pin location */
pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin));
+ spin_lock_irqsave(&qe_pio_locks[port], flags);
+
tmp_val = in_be32(&par_io[port].cpdata);
if (val == 0) /* clear */
@@ -148,10 +180,25 @@ int par_io_data_set(u8 port, u8 pin, u8 val)
else /* set */
out_be32(&par_io[port].cpdata, pin_mask | tmp_val);
+ spin_unlock_irqrestore(&qe_pio_locks[port], flags);
return 0;
}
EXPORT_SYMBOL(par_io_data_set);
+static inline int par_io_data_get(u8 port, u8 pin)
+{
+ u32 pin_mask;
+
+ if (port >= num_par_io_ports)
+ return -EINVAL;
+ if (pin >= NUM_OF_PINS)
+ return -EINVAL;
+ /* calculate pin location */
+ pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin));
+
+ return !!(in_be32(&par_io[port].cpdata) & pin_mask);
+}
+
int par_io_of_config(struct device_node *np)
{
struct device_node *pio;
@@ -195,6 +242,48 @@ int par_io_of_config(struct device_node *np)
}
EXPORT_SYMBOL(par_io_of_config);
+int gpio_request(unsigned int gpio, const char *label)
+{
+ if (!qe_pio_locks)
+ return -ENODEV;
+
+ if (gpio / 32 > num_par_io_ports)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+int gpio_direction_input(unsigned int gpio)
+{
+ return par_io_config_pin(gpio / 32, gpio % 32, 2, 0, 0, 0);
+}
+EXPORT_SYMBOL_GPL(gpio_direction_input);
+
+int gpio_direction_output(unsigned int gpio, int value)
+{
+ int ret;
+
+ ret = par_io_config_pin(gpio / 32, gpio % 32, 1, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ return par_io_data_set(gpio / 32, gpio % 32, value);
+}
+EXPORT_SYMBOL_GPL(gpio_direction_output);
+
+int gpio_get_value(unsigned int gpio)
+{
+ return par_io_data_get(gpio / 32, gpio % 32);
+}
+EXPORT_SYMBOL_GPL(gpio_get_value);
+
+int gpio_set_value(unsigned int gpio, int value)
+{
+ return par_io_data_set(gpio / 32, gpio % 32, value);
+}
+EXPORT_SYMBOL_GPL(gpio_set_value);
+
#ifdef DEBUG
static void dump_par_io(void)
{
--
1.5.2.2
^ permalink raw reply related
* [PATCH RFC 3/7] [POWERPC] CPM2: implement GPIO API
From: Anton Vorontsov @ 2007-12-10 20:48 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
arch/powerpc/platforms/Kconfig | 1 +
arch/powerpc/sysdev/cpm2_common.c | 142 +++++++++++++++++++++++++++++++++++++
2 files changed, 143 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 3d9ff27..cc2d54e 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -277,6 +277,7 @@ config CPM2
default n
select CPM
select PPC_LIB_RHEAP
+ select GENERIC_GPIO
help
The CPM2 (Communications Processor Module) is a coprocessor on
embedded CPUs made by Freescale. Selecting this option means that
diff --git a/arch/powerpc/sysdev/cpm2_common.c b/arch/powerpc/sysdev/cpm2_common.c
index 859362f..33b99d4 100644
--- a/arch/powerpc/sysdev/cpm2_common.c
+++ b/arch/powerpc/sysdev/cpm2_common.c
@@ -31,12 +31,14 @@
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
+#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/gpio.h>
#include <asm/mpc8260.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -61,9 +63,62 @@ cpm2_map_t __iomem *cpm2_immr;
of space for CPM as it is larger
than on PQ2 */
+static spinlock_t *cpm2_port_locks;
+static int cpm2_num_ports;
+
+static int par_io_xlate(struct device_node *np, int index)
+{
+ return __of_parse_gpio_bank_pin(np, index, 32, cpm2_num_ports);
+}
+
+static struct of_gpio_chip of_gpio_chip = {
+ .xlate = par_io_xlate,
+};
+
+int cpm2_init_par_io(void)
+{
+ int ret;
+ struct device_node *np;
+ const u32 *num_ports;
+ int i;
+
+ np = of_find_node_by_name(NULL, "par_io");
+ if (!np) {
+ ret = -ENOENT;
+ goto err0;
+ }
+
+ num_ports = of_get_property(np, "num-ports", NULL);
+ if (!num_ports) {
+ ret = -ENOENT;
+ goto err1;
+ }
+
+ cpm2_num_ports = *num_ports;
+ cpm2_port_locks = kzalloc(sizeof(*cpm2_port_locks) * cpm2_num_ports,
+ GFP_KERNEL);
+ if (!cpm2_port_locks) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ for (i = 0; i < cpm2_num_ports; i++)
+ spin_lock_init(&cpm2_port_locks[i]);
+
+ np->data = &of_gpio_chip;
+
+ return 0;
+err1:
+ of_node_put(np);
+err0:
+ return ret;
+}
+
void
cpm2_reset(void)
{
+ int ret;
+
#ifdef CONFIG_PPC_85xx
cpm2_immr = ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE);
#else
@@ -81,6 +136,10 @@ cpm2_reset(void)
/* Tell everyone where the comm processor resides.
*/
cpmp = &cpm2_immr->im_cpm;
+
+ ret = cpm2_init_par_io();
+ if (ret)
+ pr_warning("CPM2 PIO not initialized!\n");
}
/* Set a baud rate generator. This needs lots of work. There are
@@ -444,3 +503,86 @@ void cpm2_set_pin(int port, int pin, int flags)
else
clrbits32(&iop[port].odr, pin);
}
+
+int gpio_request(unsigned int gpio, const char *label)
+{
+ if (!cpm2_port_locks)
+ return -ENODEV;
+
+ if (gpio / 32 > cpm2_num_ports)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+int gpio_direction_input(unsigned int gpio)
+{
+ unsigned long flags;
+ int port = gpio / 32;
+ int pin = gpio % 32;
+
+ spin_lock_irqsave(&cpm2_port_locks[port], flags);
+
+ cpm2_set_pin(port, pin, CPM_PIN_INPUT | CPM_PIN_GPIO);
+
+ spin_unlock_irqrestore(&cpm2_port_locks[port], flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_input);
+
+int gpio_direction_output(unsigned int gpio, int value)
+{
+ struct cpm2_ioports __iomem *iop =
+ (struct cpm2_ioports __iomem *)&cpm2_immr->im_ioport;
+ int port = gpio / 32;
+ int pin = gpio % 32;
+ unsigned long flags;
+
+ pin = 1 << (31 - pin);
+
+ spin_lock_irqsave(&cpm2_port_locks[port], flags);
+
+ cpm2_set_pin(port, pin, CPM_PIN_OUTPUT | CPM_PIN_GPIO);
+ if (value)
+ setbits32(&iop[port].dat, pin);
+ else
+ clrbits32(&iop[port].dat, pin);
+
+ spin_unlock_irqrestore(&cpm2_port_locks[port], flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_output);
+
+int gpio_get_value(unsigned int gpio)
+{
+ struct cpm2_ioports __iomem *iop =
+ (struct cpm2_ioports __iomem *)&cpm2_immr->im_ioport;
+ int port = gpio / 32;
+ int pin = gpio % 32;
+
+ pin = 1 << (31 - pin);
+
+ return !!(in_be32(&iop[port].dat) & pin);
+}
+EXPORT_SYMBOL_GPL(gpio_get_value);
+
+int gpio_set_value(unsigned int gpio, int value)
+{
+ struct cpm2_ioports __iomem *iop =
+ (struct cpm2_ioports __iomem *)&cpm2_immr->im_ioport;
+ int port = gpio / 32;
+ int pin = gpio % 32;
+ unsigned long flags;
+
+ pin = 1 << (31 - pin);
+
+ spin_lock_irqsave(&cpm2_port_locks[port], flags);
+ if (value)
+ setbits32(&iop[port].dat, pin);
+ else
+ clrbits32(&iop[port].dat, pin);
+ spin_unlock_irqrestore(&cpm2_port_locks[port], flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_set_value);
--
1.5.2.2
^ permalink raw reply related
* [PATCH RFC 4/7] [GPIO] Let drivers link if they support GPIO API as an addition
From: Anton Vorontsov @ 2007-12-10 20:49 UTC (permalink / raw)
To: linuxppc-dev; +Cc: dbrownell
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
Hello David,
I'm interested in your opinion about that patch. You're also Cc'ed
to patch that using that feature.
I know, currently that patch will conflict with GPIOLIB patches in -mm,
so this is only RFC.
include/asm-generic/gpio.h | 47 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 47 insertions(+), 0 deletions(-)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 2d0aab1..cf76a69 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -1,6 +1,53 @@
#ifndef _ASM_GENERIC_GPIO_H
#define _ASM_GENERIC_GPIO_H
+#ifndef CONFIG_GENERIC_GPIO
+
+/*
+ * Drivers could be gpio api aware, and at the same time, some
+ * of them can live without it, or support call-backs approach
+ * in addition. Let them link.
+ */
+static inline int gpio_request(unsigned int gpio, const char *label)
+{
+ return -ENOSYS;
+}
+
+static inline void gpio_free(unsigned int gpio)
+{
+}
+
+static inline int gpio_direction_input(unsigned int gpio)
+{
+ return -ENOSYS;
+}
+
+static inline int gpio_direction_output(unsigned int gpio, int value)
+{
+ return -ENOSYS;
+}
+
+static inline int gpio_get_value(unsigned int gpio)
+{
+ return -ENOSYS;
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+}
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+ return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_GENERIC_GPIO */
+
/* platforms that don't directly support access to GPIOs through I2C, SPI,
* or other blocking infrastructure can use these wrappers.
*/
--
1.5.2.2
^ permalink raw reply related
* [PATCH RFC 5/7] [POWERPC] FSL UPM: routines to manage FSL UPMs
From: Anton Vorontsov @ 2007-12-10 20:49 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>
Here are few routines needed to manage FSL UPMs properly. FSL UPM
infrastructure also supports lockless variant, to use when we're
pretty sure that only one UPM is used on the board.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
arch/powerpc/Kconfig | 7 +++
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/fsl_upm.c | 74 +++++++++++++++++++++++++++++
include/asm-powerpc/fsl_upm.h | 102 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 184 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/fsl_upm.c
create mode 100644 include/asm-powerpc/fsl_upm.h
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 596982f..1d47a35 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -464,6 +464,13 @@ config FSL_PCI
bool
select PPC_INDIRECT_PCI
+config FSL_UPM
+ bool
+
+config FSL_UPM_LOCKLESS
+ depends on FSL_UPM
+ bool
+
# Yes MCA RS/6000s exist but Linux-PPC does not currently support any
config MCA
bool
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 99a77d7..98dbfdd 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
obj-$(CONFIG_FSL_SOC) += fsl_soc.o
obj-$(CONFIG_FSL_PCI) += fsl_pci.o
+obj-$(CONFIG_FSL_UPM) += fsl_upm.o
obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
obj-$(CONFIG_QUICC_ENGINE) += qe_lib/
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
diff --git a/arch/powerpc/sysdev/fsl_upm.c b/arch/powerpc/sysdev/fsl_upm.c
new file mode 100644
index 0000000..98d079d
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_upm.c
@@ -0,0 +1,74 @@
+/*
+ * Freescale UPM routines.
+ *
+ * Copyright (c) 2007 MontaVista Software, Inc.
+ * Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <asm/fsl_upm.h>
+
+int fsl_upm_get_for(struct device_node *node, const char *name,
+ struct fsl_upm *upm)
+{
+ int ret;
+ struct device_node *lbus;
+ struct resource lbc_res;
+ ptrdiff_t mxmr_offs;
+
+ lbus = of_get_parent(node);
+ if (!lbus) {
+ pr_err("FSL UPM: can't get parent local bus node\n");
+ return -ENOENT;
+ }
+
+ ret = of_address_to_resource(lbus, 0, &lbc_res);
+ if (ret) {
+ pr_err("FSL UPM: can't get parent local bus base\n");
+ return -ENOMEM;
+ }
+
+ switch (name[0]) {
+ case 'A':
+ mxmr_offs = LBC_MAMR;
+ break;
+ case 'B':
+ mxmr_offs = LBC_MBMR;
+ break;
+ case 'C':
+ mxmr_offs = LBC_MCMR;
+ break;
+ default:
+ pr_err("FSL UPM: unknown UPM requested\n");
+ return -EINVAL;
+ break;
+ }
+
+ upm->lbc_base = ioremap_nocache(lbc_res.start,
+ lbc_res.end - lbc_res.start + 1);
+ if (!upm->lbc_base)
+ return -ENOMEM;
+
+ upm->mxmr = upm->lbc_base + mxmr_offs;
+ upm->mar = upm->lbc_base + LBC_MAR;
+
+ return 0;
+}
+
+#ifndef CONFIG_FSL_UPM_LOCKLESS
+spinlock_t upm_lock;
+unsigned long upm_lock_flags;
+
+static int __init fsl_upm_init(void)
+{
+ spin_lock_init(&upm_lock);
+ return 0;
+}
+arch_initcall(fsl_upm_init);
+#endif
diff --git a/include/asm-powerpc/fsl_upm.h b/include/asm-powerpc/fsl_upm.h
new file mode 100644
index 0000000..19f5f9d
--- /dev/null
+++ b/include/asm-powerpc/fsl_upm.h
@@ -0,0 +1,102 @@
+/*
+ * Freescale UPM routines.
+ *
+ * Copyright (c) 2007 MontaVista Software, Inc.
+ * Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __ASM_POWERPC_FSL_UPM
+#define __ASM_POWERPC_FSL_UPM
+
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#define LBC_MAR 0x68
+#define LBC_MAMR 0x70
+#define LBC_MBMR 0x74
+#define LBC_MCMR 0x78
+
+#define LBC_MXMR_RUNP 0x30000000
+
+struct fsl_upm {
+ void __iomem *lbc_base;
+ void __iomem *mxmr;
+ void __iomem *mar;
+};
+
+#ifndef CONFIG_FSL_UPM_LOCKLESS
+extern spinlock_t upm_lock;
+extern unsigned long upm_lock_flags;
+
+static inline void upm_do_lock(void)
+{
+ spin_lock_irqsave(&upm_lock, upm_lock_flags);
+}
+
+static inline void upm_do_unlock(void)
+{
+ spin_unlock_irqrestore(&upm_lock, upm_lock_flags);
+}
+#else /* CONFIG_FSL_UPM_LOCKLESS */
+static inline void upm_do_lock(void) {}
+static inline void upm_do_unlock(void) {}
+#endif /* CONFIG_FSL_UPM_LOCKLESS */
+
+extern int fsl_upm_get_for(struct device_node *node, const char *name,
+ struct fsl_upm *upm);
+
+static inline void fsl_upm_free(struct fsl_upm *upm)
+{
+ iounmap(upm->lbc_base);
+}
+
+static inline int fsl_upm_got(struct fsl_upm *upm)
+{
+ return !!upm->lbc_base;
+}
+
+static inline void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset)
+{
+ upm_do_lock();
+ out_be32(upm->mxmr, LBC_MXMR_RUNP | pat_offset);
+}
+
+static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
+{
+ out_be32(upm->mxmr, 0x0);
+
+ while (in_be32(upm->mxmr) != 0x0)
+ cpu_relax();
+
+ upm_do_unlock();
+}
+
+static inline int fsl_upm_run_pattern(struct fsl_upm *upm,
+ void __iomem *io_base,
+ int width, u32 cmd)
+{
+ out_be32(upm->mar, cmd << (32 - width));
+ switch (width) {
+ case 8:
+ out_8(io_base, 0x0);
+ break;
+ case 16:
+ out_be16(io_base, 0x0);
+ break;
+ case 32:
+ out_be32(io_base, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* __ASM_POWERPC_FSL_UPM */
--
1.5.2.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox