diff options
Diffstat (limited to 'drivers/mfd/tc3589x.c')
| -rw-r--r-- | drivers/mfd/tc3589x.c | 228 |
1 files changed, 167 insertions, 61 deletions
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index de979742c6f..bd83accc0f6 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -9,10 +9,27 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/mfd/core.h> #include <linux/mfd/tc3589x.h> +#include <linux/err.h> + +/** + * enum tc3589x_version - indicates the TC3589x version + */ +enum tc3589x_version { + TC3589X_TC35890, + TC3589X_TC35892, + TC3589X_TC35893, + TC3589X_TC35894, + TC3589X_TC35895, + TC3589X_TC35896, + TC3589X_UNKNOWN, +}; #define TC3589x_CLKMODE_MODCTL_SLEEP 0x0 #define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0) @@ -140,19 +157,21 @@ static struct resource keypad_resources[] = { }, }; -static struct mfd_cell tc3589x_dev_gpio[] = { +static const struct mfd_cell tc3589x_dev_gpio[] = { { .name = "tc3589x-gpio", .num_resources = ARRAY_SIZE(gpio_resources), .resources = &gpio_resources[0], + .of_compatible = "toshiba,tc3589x-gpio", }, }; -static struct mfd_cell tc3589x_dev_keypad[] = { +static const struct mfd_cell tc3589x_dev_keypad[] = { { .name = "tc3589x-keypad", .num_resources = ARRAY_SIZE(keypad_resources), .resources = &keypad_resources[0], + .of_compatible = "toshiba,tc3589x-keypad", }, }; @@ -168,8 +187,9 @@ again: while (status) { int bit = __ffs(status); + int virq = irq_create_mapping(tc3589x->domain, bit); - handle_nested_irq(tc3589x->irq_base + bit); + handle_nested_irq(virq); status &= ~(1 << bit); } @@ -186,38 +206,53 @@ again: return IRQ_HANDLED; } -static int tc3589x_irq_init(struct tc3589x *tc3589x) +static int tc3589x_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { - int base = tc3589x->irq_base; - int irq; + struct tc3589x *tc3589x = d->host_data; - for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { - irq_set_chip_data(irq, tc3589x); - irq_set_chip_and_handler(irq, &dummy_irq_chip, - handle_edge_irq); - irq_set_nested_thread(irq, 1); + irq_set_chip_data(virq, tc3589x); + irq_set_chip_and_handler(virq, &dummy_irq_chip, + handle_edge_irq); + irq_set_nested_thread(virq, 1); #ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); #else - irq_set_noprobe(irq); + irq_set_noprobe(virq); #endif - } return 0; } -static void tc3589x_irq_remove(struct tc3589x *tc3589x) +static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq) { - int base = tc3589x->irq_base; - int irq; - - for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { #ifdef CONFIG_ARM - set_irq_flags(irq, 0); + set_irq_flags(virq, 0); #endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); + irq_set_chip_and_handler(virq, NULL, NULL); + irq_set_chip_data(virq, NULL); +} + +static struct irq_domain_ops tc3589x_irq_ops = { + .map = tc3589x_irq_map, + .unmap = tc3589x_irq_unmap, + .xlate = irq_domain_xlate_twocell, +}; + +static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np) +{ + int base = tc3589x->irq_base; + + tc3589x->domain = irq_domain_add_simple( + np, TC3589x_NR_INTERNAL_IRQS, base, + &tc3589x_irq_ops, tc3589x); + + if (!tc3589x->domain) { + dev_err(tc3589x->dev, "Failed to create irqdomain\n"); + return -ENOSYS; } + + return 0; } static int tc3589x_chip_init(struct tc3589x *tc3589x) @@ -255,15 +290,15 @@ static int tc3589x_chip_init(struct tc3589x *tc3589x) return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); } -static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) +static int tc3589x_device_init(struct tc3589x *tc3589x) { int ret = 0; unsigned int blocks = tc3589x->pdata->block; if (blocks & TC3589x_BLOCK_GPIO) { ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, - ARRAY_SIZE(tc3589x_dev_gpio), NULL, - tc3589x->irq_base); + ARRAY_SIZE(tc3589x_dev_gpio), NULL, + tc3589x->irq_base, tc3589x->domain); if (ret) { dev_err(tc3589x->dev, "failed to add gpio child\n"); return ret; @@ -273,8 +308,8 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) if (blocks & TC3589x_BLOCK_KEYPAD) { ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, - ARRAY_SIZE(tc3589x_dev_keypad), NULL, - tc3589x->irq_base); + ARRAY_SIZE(tc3589x_dev_keypad), NULL, + tc3589x->irq_base, tc3589x->domain); if (ret) { dev_err(tc3589x->dev, "failed to keypad child\n"); return ret; @@ -285,18 +320,82 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) return ret; } -static int __devinit tc3589x_probe(struct i2c_client *i2c, +#ifdef CONFIG_OF +static const struct of_device_id tc3589x_match[] = { + /* Legacy compatible string */ + { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN }, + { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 }, + { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 }, + { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 }, + { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 }, + { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 }, + { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 }, + { } +}; + +MODULE_DEVICE_TABLE(of, tc3589x_match); + +static struct tc3589x_platform_data * +tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) +{ + struct device_node *np = dev->of_node; + struct tc3589x_platform_data *pdata; + struct device_node *child; + const struct of_device_id *of_id; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + of_id = of_match_device(tc3589x_match, dev); + if (!of_id) + return ERR_PTR(-ENODEV); + *version = (enum tc3589x_version) of_id->data; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, "toshiba,tc3589x-gpio")) + pdata->block |= TC3589x_BLOCK_GPIO; + if (of_device_is_compatible(child, "toshiba,tc3589x-keypad")) + pdata->block |= TC3589x_BLOCK_KEYPAD; + } + + return pdata; +} +#else +static inline struct tc3589x_platform_data * +tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) +{ + dev_err(dev, "no device tree support\n"); + return ERR_PTR(-ENODEV); +} +#endif + +static int tc3589x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct tc3589x_platform_data *pdata = i2c->dev.platform_data; + struct device_node *np = i2c->dev.of_node; + struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev); struct tc3589x *tc3589x; + enum tc3589x_version version; int ret; + if (!pdata) { + pdata = tc3589x_of_probe(&i2c->dev, &version); + if (IS_ERR(pdata)) { + dev_err(&i2c->dev, "No platform data or DT found\n"); + return PTR_ERR(pdata); + } + } else { + /* When not probing from device tree we have this ID */ + version = id->driver_data; + } + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; - tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL); + tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x), + GFP_KERNEL); if (!tc3589x) return -ENOMEM; @@ -306,58 +405,59 @@ static int __devinit tc3589x_probe(struct i2c_client *i2c, tc3589x->i2c = i2c; tc3589x->pdata = pdata; tc3589x->irq_base = pdata->irq_base; - tc3589x->num_gpio = id->driver_data; + + switch (version) { + case TC3589X_TC35893: + case TC3589X_TC35895: + case TC3589X_TC35896: + tc3589x->num_gpio = 20; + break; + case TC3589X_TC35890: + case TC3589X_TC35892: + case TC3589X_TC35894: + case TC3589X_UNKNOWN: + default: + tc3589x->num_gpio = 24; + break; + } i2c_set_clientdata(i2c, tc3589x); ret = tc3589x_chip_init(tc3589x); if (ret) - goto out_free; + return ret; - ret = tc3589x_irq_init(tc3589x); + ret = tc3589x_irq_init(tc3589x, np); if (ret) - goto out_free; + return ret; ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "tc3589x", tc3589x); if (ret) { dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); - goto out_removeirq; + return ret; } ret = tc3589x_device_init(tc3589x); if (ret) { dev_err(tc3589x->dev, "failed to add child devices\n"); - goto out_freeirq; + return ret; } return 0; - -out_freeirq: - free_irq(tc3589x->i2c->irq, tc3589x); -out_removeirq: - tc3589x_irq_remove(tc3589x); -out_free: - kfree(tc3589x); - return ret; } -static int __devexit tc3589x_remove(struct i2c_client *client) +static int tc3589x_remove(struct i2c_client *client) { struct tc3589x *tc3589x = i2c_get_clientdata(client); mfd_remove_devices(tc3589x->dev); - free_irq(tc3589x->i2c->irq, tc3589x); - tc3589x_irq_remove(tc3589x); - - kfree(tc3589x); - return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int tc3589x_suspend(struct device *dev) { struct tc3589x *tc3589x = dev_get_drvdata(dev); @@ -385,25 +485,31 @@ static int tc3589x_resume(struct device *dev) return ret; } - -static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, - tc3589x_resume); #endif +static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume); + static const struct i2c_device_id tc3589x_id[] = { - { "tc3589x", 24 }, + { "tc35890", TC3589X_TC35890 }, + { "tc35892", TC3589X_TC35892 }, + { "tc35893", TC3589X_TC35893 }, + { "tc35894", TC3589X_TC35894 }, + { "tc35895", TC3589X_TC35895 }, + { "tc35896", TC3589X_TC35896 }, + { "tc3589x", TC3589X_UNKNOWN }, { } }; MODULE_DEVICE_TABLE(i2c, tc3589x_id); static struct i2c_driver tc3589x_driver = { - .driver.name = "tc3589x", - .driver.owner = THIS_MODULE, -#ifdef CONFIG_PM - .driver.pm = &tc3589x_dev_pm_ops, -#endif + .driver = { + .name = "tc3589x", + .owner = THIS_MODULE, + .pm = &tc3589x_dev_pm_ops, + .of_match_table = of_match_ptr(tc3589x_match), + }, .probe = tc3589x_probe, - .remove = __devexit_p(tc3589x_remove), + .remove = tc3589x_remove, .id_table = tc3589x_id, }; |
