From: David Brownell Date: Sun, 21 Sep 2008 21:08:47 +0000 (-0700) Subject: twl4030: new-style driver conversion X-Git-Tag: v2.6.27-omap1~151 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=3d1322663fef6c9b1477c744f03d781e3f531239;p=linux-2.6-omap-h63xx.git twl4030: new-style driver conversion More updates preparing for upstream merge of twl4030 driver: Basic fixes - twl4030-core becomes a new-style I2C driver - declare and use platform_data structure for that core - stop hard-wiring the irq numbers - check functionality that's really used: I2C, not SMBus Cleanup: - remove needless "client string" - remove some fake "bool" types - include catalog part numbers (TPS659{5,3,2}0) - diagnostics should identify the driver! To use this, all boards with these chips will need to declare this chip when they declare their active I2C busses (done in a separate patch). NOTE: the TWL4030_IRQ_* symbols still need to vanish, along with other global resource assignments associated with these chips. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren --- diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c index 8d7c5efd07f..07d00476835 100644 --- a/drivers/i2c/chips/twl4030-core.c +++ b/drivers/i2c/chips/twl4030-core.c @@ -1,5 +1,5 @@ /* - * twl4030_core.c - driver for TWL4030 PM and audio CODEC device + * twl4030_core.c - driver for TWL4030/TPS659x0 PM and audio CODEC devices * * Copyright (C) 2005-2006 Texas Instruments, Inc. * @@ -56,14 +56,6 @@ #define DRIVER_NAME "twl4030" -/* Macro Definitions */ -#define TWL_CLIENT_STRING "TWL4030-ID" -#define TWL_CLIENT_USED 1 -#define TWL_CLIENT_FREE 0 - -/* IRQ Flags */ -#define FREE 0 -#define USED 1 /* Primary Interrupt Handler on TWL4030 Registers */ @@ -293,26 +285,18 @@ static const struct twl4030_mod_iregs __initconst twl4030_mod_regs[] = { /* Helper functions */ -static int -twl4030_detect_client(struct i2c_adapter *adapter, unsigned char sid); -static int twl4030_attach_adapter(struct i2c_adapter *adapter); -static int twl4030_detach_client(struct i2c_client *client); static void do_twl4030_irq(unsigned int irq, irq_desc_t *desc); -static void twl_init_irq(void); - /* Data Structures */ /* To have info on T2 IRQ substem activated or not */ -static unsigned char twl_irq_used = FREE; static struct completion irq_event; /* Structure to define on TWL4030 Slave ID */ struct twl4030_client { - struct i2c_client client; - const char client_name[sizeof(TWL_CLIENT_STRING) + 1]; + struct i2c_client *client; const unsigned char address; const char adapter_index; - unsigned char inuse; + bool inuse; /* max numb of i2c_msg required is for read =2 */ struct i2c_msg xfer_msg[2]; @@ -356,36 +340,22 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { static struct twl4030_client twl4030_modules[TWL4030_NUM_SLAVES] = { { .address = TWL4030_SLAVEID_ID0, - .client_name = TWL_CLIENT_STRING "0", .adapter_index = CONFIG_I2C_TWL4030_ID, }, { .address = TWL4030_SLAVEID_ID1, - .client_name = TWL_CLIENT_STRING "1", .adapter_index = CONFIG_I2C_TWL4030_ID, }, { .address = TWL4030_SLAVEID_ID2, - .client_name = TWL_CLIENT_STRING "2", .adapter_index = CONFIG_I2C_TWL4030_ID, }, { .address = TWL4030_SLAVEID_ID3, - .client_name = TWL_CLIENT_STRING "3", .adapter_index = CONFIG_I2C_TWL4030_ID, }, }; -/* One Client Driver , 4 Clients */ -static struct i2c_driver twl4030_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, - .attach_adapter = twl4030_attach_adapter, - .detach_client = twl4030_detach_client, -}; - /* * TWL4030 doesn't have PIH mask, hence dummy function for mask * and unmask. @@ -425,15 +395,14 @@ int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes) struct i2c_msg *msg; if (unlikely(mod_no > TWL4030_MODULE_LAST)) { - pr_err("Invalid module Number\n"); + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } sid = twl4030_map[mod_no].sid; twl = &twl4030_modules[sid]; - if (unlikely(twl->inuse != TWL_CLIENT_USED)) { - pr_err("I2C Client[%d] is not initialized[%d]\n", - sid, __LINE__); + if (unlikely(!twl->inuse)) { + pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid); return -EPERM; } mutex_lock(&twl->xfer_lock); @@ -448,7 +417,7 @@ int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes) msg->buf = value; /* over write the first byte of buffer with the register address */ *value = twl4030_map[mod_no].base + reg; - ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1); + ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1); mutex_unlock(&twl->xfer_lock); /* i2cTransfer returns num messages.translate it pls.. */ @@ -476,15 +445,14 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes) struct i2c_msg *msg; if (unlikely(mod_no > TWL4030_MODULE_LAST)) { - pr_err("Invalid module Number\n"); + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } sid = twl4030_map[mod_no].sid; twl = &twl4030_modules[sid]; - if (unlikely(twl->inuse != TWL_CLIENT_USED)) { - pr_err("I2C Client[%d] is not initialized[%d]\n", sid, - __LINE__); + if (unlikely(!twl->inuse)) { + pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid); return -EPERM; } mutex_lock(&twl->xfer_lock); @@ -501,7 +469,7 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes) msg->flags = I2C_M_RD; /* Read the register value */ msg->len = num_bytes; /* only n bytes */ msg->buf = value; - ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2); + ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2); mutex_unlock(&twl->xfer_lock); /* i2cTransfer returns num messages.translate it pls.. */ @@ -608,6 +576,8 @@ static void do_twl4030_module_irq(unsigned int irq, irq_desc_t *desc) " be masked!\n", irq); } +static unsigned twl4030_irq_base; + /* * twl4030_irq_thread() runs as a kernel thread. It queries the twl4030 * interrupt controller to see which modules are generating interrupt requests @@ -644,7 +614,7 @@ static int twl4030_irq_thread(void *data) continue; } - for (module_irq = TWL4030_IRQ_BASE; 0 != pih_isr; + for (module_irq = twl4030_irq_base; 0 != pih_isr; pih_isr >>= 1, module_irq++) { if (pih_isr & 0x1) { irq_desc_t *d = irq_desc + module_irq; @@ -693,78 +663,17 @@ static void do_twl4030_irq(unsigned int irq, irq_desc_t *desc) } } -/* attach a client to the adapter */ -static int __init twl4030_detect_client(struct i2c_adapter *adapter, - unsigned char sid) -{ - int err = 0; - struct twl4030_client *twl; - - if (unlikely(sid >= TWL4030_NUM_SLAVES)) { - pr_err("sid[%d] > MOD_LAST[%d]\n", sid, TWL4030_NUM_SLAVES); - return -EPERM; - } - - /* Check basic functionality */ - err = i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_WORD_DATA - | I2C_FUNC_SMBUS_WRITE_BYTE); - if (!err) { - pr_err("SlaveID=%d functionality check failed\n", sid); - return err; - } - twl = &twl4030_modules[sid]; - if (unlikely(twl->inuse)) { - pr_err("Client %s is already in use\n", twl->client_name); - return -EPERM; - } - - memset(&twl->client, 0, sizeof(struct i2c_client)); - - twl->client.addr = twl->address; - twl->client.adapter = adapter; - twl->client.driver = &twl4030_driver; - - memcpy(&twl->client.name, twl->client_name, - sizeof(TWL_CLIENT_STRING) + 1); - - pr_info("TWL4030: TRY attach Slave %s on Adapter %s [%x]\n", - twl->client_name, adapter->name, err); - - err = i2c_attach_client(&twl->client); - if (err) { - pr_err("Couldn't attach Slave %s on Adapter" - "%s [%x]\n", twl->client_name, adapter->name, err); - } else { - twl->inuse = TWL_CLIENT_USED; - mutex_init(&twl->xfer_lock); - } - - return err; -} - -static int add_children(void) +static int add_children(struct twl4030_platform_data *pdata) { - static bool children; - struct platform_device *pdev = NULL; struct twl4030_client *twl = NULL; int status = 0; - /* FIXME this doesn't yet set up platform_data for anything; - * it can't be available until this becomes a "new style" - * I2C driver. Similarly, a new style driver will know it - * didn't already initialize its children. - */ - - if (children) - return 0; - #ifdef CONFIG_RTC_DRV_TWL4030 pdev = platform_device_alloc("twl4030_rtc", -1); if (pdev) { twl = &twl4030_modules[TWL4030_SLAVENUM_NUM3]; - pdev->dev.parent = &twl->client.dev; + pdev->dev.parent = &twl->client->dev; device_init_wakeup(&pdev->dev, 1); /* @@ -788,66 +697,9 @@ static int add_children(void) status = -ENOMEM; #endif - children = true; return status; } -/* adapter callback */ -static int __init twl4030_attach_adapter(struct i2c_adapter *adapter) -{ - int i; - int ret = 0; - static int twl_i2c_adapter = 1; - - for (i = 0; i < TWL4030_NUM_SLAVES; i++) { - /* Check if I need to hook on to this adapter or not */ - if (twl4030_modules[i].adapter_index == twl_i2c_adapter) { - ret = twl4030_detect_client(adapter, i); - if (ret) - goto free_client; - } - } - twl_i2c_adapter++; - - add_children(); - - /* - * Check if the PIH module is initialized, if yes, then init - * the T2 Interrupt subsystem - */ - if ((twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse == - TWL_CLIENT_USED) && (twl_irq_used != USED)) { - twl_init_irq(); - twl_irq_used = USED; - } - return 0; - -free_client: - pr_err("TWL_CLIENT(Idx=%d] registration failed[0x%x]\n", i, ret); - - /* ignore current slave..it never got registered */ - i--; - while (i >= 0) { - /* now remove all those from the current adapter... */ - if (twl4030_modules[i].adapter_index == twl_i2c_adapter) - (void)twl4030_detach_client(&twl4030_modules[i].client); - i--; - } - return ret; -} - -/* adapter's callback */ -static int twl4030_detach_client(struct i2c_client *client) -{ - int err; - err = i2c_detach_client(client); - if (err) { - pr_err("Client detach failed\n"); - return err; - } - return 0; -} - static struct task_struct * __init start_twl4030_irq_thread(int irq) { struct task_struct *thread; @@ -1005,12 +857,11 @@ static void __init twl4030_mask_clear_intrs(const struct twl4030_mod_iregs *t, } -static void twl_init_irq(void) +static void twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) { int i; int res = 0; char *msg = "Unable to register interrupt subsystem"; - unsigned int irq_num; /* * Mask and clear all TWL4030 interrupts since initially we do @@ -1019,15 +870,15 @@ static void twl_init_irq(void) twl4030_mask_clear_intrs(twl4030_mod_regs, ARRAY_SIZE(twl4030_mod_regs)); + twl4030_irq_base = irq_base; + /* install an irq handler for each of the PIH modules */ - for (i = TWL4030_IRQ_BASE; i < TWL4030_IRQ_END; i++) { + for (i = irq_base; i < irq_end; i++) { set_irq_chip(i, &twl4030_irq_chip); set_irq_handler(i, do_twl4030_module_irq); set_irq_flags(i, IRQF_VALID); } - irq_num = (cpu_is_omap2430()) ? INT_24XX_SYS_NIRQ : INT_34XX_SYS_NIRQ; - /* install an irq handler to demultiplex the TWL4030 interrupt */ set_irq_data(irq_num, start_twl4030_irq_thread(irq_num)); set_irq_type(irq_num, IRQ_TYPE_EDGE_FALLING); @@ -1035,24 +886,133 @@ static void twl_init_irq(void) res = power_companion_init(); if (res < 0) - pr_err("%s[%d][%d]\n", msg, res, __LINE__); + pr_err("%s: %s[%d]\n", DRIVER_NAME, msg, res); } +/*----------------------------------------------------------------------*/ + +static int twl4030_remove(struct i2c_client *client) +{ + unsigned i; + + /* FIXME undo twl_init_irq() */ + if (twl4030_irq_base) { + dev_err(&client->dev, "can't yet clean up IRQs?\n"); + return -ENOSYS; + } + + for (i = 0; i < TWL4030_NUM_SLAVES; i++) { + struct twl4030_client *twl = &twl4030_modules[i]; + + if (twl->client && twl->client != client) + i2c_unregister_device(twl->client); + twl4030_modules[i].client = NULL; + twl4030_modules[i].inuse = false; + } + return 0; +} + +/* NOTE: this driver only handles a single twl4030/tps659x0 chip */ +static int +twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int status; + unsigned i; + struct twl4030_platform_data *pdata = client->dev.platform_data; + + if (!pdata) { + dev_dbg(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "can't talk I2C?\n"); + return -EIO; + } + + for (i = 0; i < TWL4030_NUM_SLAVES; i++) { + if (twl4030_modules[i].inuse || twl4030_irq_base) { + dev_dbg(&client->dev, "driver is already in use\n"); + return -EBUSY; + } + } + + for (i = 0; i < TWL4030_NUM_SLAVES; i++) { + struct twl4030_client *twl = &twl4030_modules[i]; + + if (i == 0) + twl->client = client; + else { + twl->client = i2c_new_dummy(client->adapter, + twl->address); + if (!twl->client) { + dev_err(&twl->client->dev, + "can't attach client %d\n", i); + status = -ENOMEM; + goto fail; + } + strlcpy(twl->client->name, id->name, + sizeof(twl->client->name)); + } + twl->inuse = true; + mutex_init(&twl->xfer_lock); + } + + status = add_children(pdata); + if (status < 0) + goto fail; + + /* + * Check if the PIH module is initialized, if yes, then init + * the T2 Interrupt subsystem + */ + if (twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse + && twl4030_irq_base == 0 + && client->irq + && pdata->irq_base + && pdata->irq_end > pdata->irq_base) + twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end); + + dev_info(&client->dev, "chaining %d irqs\n", + twl4030_irq_base + ? (pdata->irq_end - pdata->irq_base) + : 0); + return 0; + +fail: + twl4030_remove(client); + return status; +} + +static const struct i2c_device_id twl4030_ids[] = { + { "twl4030", 0 }, /* "Triton 2" */ + { "tps65950", 0 }, /* catalog version of twl4030 */ + { "tps65930", 0 }, /* fewer LDOs and DACs; no charger */ + { "tps65920", 0 }, /* fewer LDOs; no codec or charger */ + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(i2c, twl4030_ids); + +/* One Client Driver , 4 Clients */ +static struct i2c_driver twl4030_driver = { + .driver.name = DRIVER_NAME, + .id_table = twl4030_ids, + .probe = twl4030_probe, + .remove = twl4030_remove, +}; + static int __init twl4030_init(void) { return i2c_add_driver(&twl4030_driver); } +subsys_initcall(twl4030_init); static void __exit twl4030_exit(void) { i2c_del_driver(&twl4030_driver); - twl_irq_used = FREE; } - -subsys_initcall(twl4030_init); module_exit(twl4030_exit); -MODULE_ALIAS("i2c:" DRIVER_NAME); MODULE_AUTHOR("Texas Instruments, Inc."); MODULE_DESCRIPTION("I2C Core interface for TWL4030"); MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index 00e14fc37f4..2434ad0a897 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h @@ -52,6 +52,17 @@ #define TWL4030_MODULE_RTC 0x14 #define TWL4030_MODULE_SECURED_REG 0x15 +struct twl4030_platform_data { + unsigned irq_base, irq_end; + + /* REVISIT more to come ... _nothing_ should be hard-wired */ +}; + +/* + * FIXME completely stop using TWL4030_IRQ_BASE ... instead, pass the + * IRQ data to subsidiary devices using platform device resources. + */ + /* IRQ information-need base */ #include /* TWL4030 interrupts */