1f4e8afdcSSundar Iyer /* 2f4e8afdcSSundar Iyer * Copyright (C) ST-Ericsson SA 2010 3f4e8afdcSSundar Iyer * 4f4e8afdcSSundar Iyer * License Terms: GNU General Public License, version 2 5f4e8afdcSSundar Iyer * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson 6f4e8afdcSSundar Iyer * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson 7f4e8afdcSSundar Iyer */ 8f4e8afdcSSundar Iyer 9f4e8afdcSSundar Iyer #include <linux/module.h> 10f4e8afdcSSundar Iyer #include <linux/interrupt.h> 11f4e8afdcSSundar Iyer #include <linux/irq.h> 1215e27b10SLee Jones #include <linux/irqdomain.h> 13f4e8afdcSSundar Iyer #include <linux/slab.h> 14f4e8afdcSSundar Iyer #include <linux/i2c.h> 15a435ae1dSLee Jones #include <linux/of.h> 16*a381b13eSLinus Walleij #include <linux/of_device.h> 17f4e8afdcSSundar Iyer #include <linux/mfd/core.h> 18f4e8afdcSSundar Iyer #include <linux/mfd/tc3589x.h> 19*a381b13eSLinus Walleij #include <linux/err.h> 20f4e8afdcSSundar Iyer 21e64c1eb4SLinus Walleij /** 22e64c1eb4SLinus Walleij * enum tc3589x_version - indicates the TC3589x version 23e64c1eb4SLinus Walleij */ 24e64c1eb4SLinus Walleij enum tc3589x_version { 25e64c1eb4SLinus Walleij TC3589X_TC35890, 26e64c1eb4SLinus Walleij TC3589X_TC35892, 27e64c1eb4SLinus Walleij TC3589X_TC35893, 28e64c1eb4SLinus Walleij TC3589X_TC35894, 29e64c1eb4SLinus Walleij TC3589X_TC35895, 30e64c1eb4SLinus Walleij TC3589X_TC35896, 31e64c1eb4SLinus Walleij TC3589X_UNKNOWN, 32e64c1eb4SLinus Walleij }; 33e64c1eb4SLinus Walleij 34593e9d70SSundar Iyer #define TC3589x_CLKMODE_MODCTL_SLEEP 0x0 35593e9d70SSundar Iyer #define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0) 36593e9d70SSundar Iyer 37f4e8afdcSSundar Iyer /** 3820406ebfSSundar Iyer * tc3589x_reg_read() - read a single TC3589x register 3920406ebfSSundar Iyer * @tc3589x: Device to read from 40f4e8afdcSSundar Iyer * @reg: Register to read 41f4e8afdcSSundar Iyer */ 4220406ebfSSundar Iyer int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg) 43f4e8afdcSSundar Iyer { 44f4e8afdcSSundar Iyer int ret; 45f4e8afdcSSundar Iyer 4620406ebfSSundar Iyer ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg); 47f4e8afdcSSundar Iyer if (ret < 0) 4820406ebfSSundar Iyer dev_err(tc3589x->dev, "failed to read reg %#x: %d\n", 49f4e8afdcSSundar Iyer reg, ret); 50f4e8afdcSSundar Iyer 51f4e8afdcSSundar Iyer return ret; 52f4e8afdcSSundar Iyer } 5320406ebfSSundar Iyer EXPORT_SYMBOL_GPL(tc3589x_reg_read); 54f4e8afdcSSundar Iyer 55f4e8afdcSSundar Iyer /** 5620406ebfSSundar Iyer * tc3589x_reg_read() - write a single TC3589x register 5720406ebfSSundar Iyer * @tc3589x: Device to write to 58f4e8afdcSSundar Iyer * @reg: Register to read 59f4e8afdcSSundar Iyer * @data: Value to write 60f4e8afdcSSundar Iyer */ 6120406ebfSSundar Iyer int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data) 62f4e8afdcSSundar Iyer { 63f4e8afdcSSundar Iyer int ret; 64f4e8afdcSSundar Iyer 6520406ebfSSundar Iyer ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data); 66f4e8afdcSSundar Iyer if (ret < 0) 6720406ebfSSundar Iyer dev_err(tc3589x->dev, "failed to write reg %#x: %d\n", 68f4e8afdcSSundar Iyer reg, ret); 69f4e8afdcSSundar Iyer 70f4e8afdcSSundar Iyer return ret; 71f4e8afdcSSundar Iyer } 7220406ebfSSundar Iyer EXPORT_SYMBOL_GPL(tc3589x_reg_write); 73f4e8afdcSSundar Iyer 74f4e8afdcSSundar Iyer /** 7520406ebfSSundar Iyer * tc3589x_block_read() - read multiple TC3589x registers 7620406ebfSSundar Iyer * @tc3589x: Device to read from 77f4e8afdcSSundar Iyer * @reg: First register 78f4e8afdcSSundar Iyer * @length: Number of registers 79f4e8afdcSSundar Iyer * @values: Buffer to write to 80f4e8afdcSSundar Iyer */ 8120406ebfSSundar Iyer int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values) 82f4e8afdcSSundar Iyer { 83f4e8afdcSSundar Iyer int ret; 84f4e8afdcSSundar Iyer 8520406ebfSSundar Iyer ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values); 86f4e8afdcSSundar Iyer if (ret < 0) 8720406ebfSSundar Iyer dev_err(tc3589x->dev, "failed to read regs %#x: %d\n", 88f4e8afdcSSundar Iyer reg, ret); 89f4e8afdcSSundar Iyer 90f4e8afdcSSundar Iyer return ret; 91f4e8afdcSSundar Iyer } 9220406ebfSSundar Iyer EXPORT_SYMBOL_GPL(tc3589x_block_read); 93f4e8afdcSSundar Iyer 94f4e8afdcSSundar Iyer /** 9520406ebfSSundar Iyer * tc3589x_block_write() - write multiple TC3589x registers 9620406ebfSSundar Iyer * @tc3589x: Device to write to 97f4e8afdcSSundar Iyer * @reg: First register 98f4e8afdcSSundar Iyer * @length: Number of registers 99f4e8afdcSSundar Iyer * @values: Values to write 100f4e8afdcSSundar Iyer */ 10120406ebfSSundar Iyer int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, 102f4e8afdcSSundar Iyer const u8 *values) 103f4e8afdcSSundar Iyer { 104f4e8afdcSSundar Iyer int ret; 105f4e8afdcSSundar Iyer 10620406ebfSSundar Iyer ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length, 107f4e8afdcSSundar Iyer values); 108f4e8afdcSSundar Iyer if (ret < 0) 10920406ebfSSundar Iyer dev_err(tc3589x->dev, "failed to write regs %#x: %d\n", 110f4e8afdcSSundar Iyer reg, ret); 111f4e8afdcSSundar Iyer 112f4e8afdcSSundar Iyer return ret; 113f4e8afdcSSundar Iyer } 11420406ebfSSundar Iyer EXPORT_SYMBOL_GPL(tc3589x_block_write); 115f4e8afdcSSundar Iyer 116f4e8afdcSSundar Iyer /** 11720406ebfSSundar Iyer * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register 11820406ebfSSundar Iyer * @tc3589x: Device to write to 119f4e8afdcSSundar Iyer * @reg: Register to write 120f4e8afdcSSundar Iyer * @mask: Mask of bits to set 121f4e8afdcSSundar Iyer * @values: Value to set 122f4e8afdcSSundar Iyer */ 12320406ebfSSundar Iyer int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val) 124f4e8afdcSSundar Iyer { 125f4e8afdcSSundar Iyer int ret; 126f4e8afdcSSundar Iyer 12720406ebfSSundar Iyer mutex_lock(&tc3589x->lock); 128f4e8afdcSSundar Iyer 12920406ebfSSundar Iyer ret = tc3589x_reg_read(tc3589x, reg); 130f4e8afdcSSundar Iyer if (ret < 0) 131f4e8afdcSSundar Iyer goto out; 132f4e8afdcSSundar Iyer 133f4e8afdcSSundar Iyer ret &= ~mask; 134f4e8afdcSSundar Iyer ret |= val; 135f4e8afdcSSundar Iyer 13620406ebfSSundar Iyer ret = tc3589x_reg_write(tc3589x, reg, ret); 137f4e8afdcSSundar Iyer 138f4e8afdcSSundar Iyer out: 13920406ebfSSundar Iyer mutex_unlock(&tc3589x->lock); 140f4e8afdcSSundar Iyer return ret; 141f4e8afdcSSundar Iyer } 14220406ebfSSundar Iyer EXPORT_SYMBOL_GPL(tc3589x_set_bits); 143f4e8afdcSSundar Iyer 144f4e8afdcSSundar Iyer static struct resource gpio_resources[] = { 145f4e8afdcSSundar Iyer { 14620406ebfSSundar Iyer .start = TC3589x_INT_GPIIRQ, 14720406ebfSSundar Iyer .end = TC3589x_INT_GPIIRQ, 148f4e8afdcSSundar Iyer .flags = IORESOURCE_IRQ, 149f4e8afdcSSundar Iyer }, 150f4e8afdcSSundar Iyer }; 151f4e8afdcSSundar Iyer 15209c730a4SSundar Iyer static struct resource keypad_resources[] = { 15309c730a4SSundar Iyer { 15409c730a4SSundar Iyer .start = TC3589x_INT_KBDIRQ, 15509c730a4SSundar Iyer .end = TC3589x_INT_KBDIRQ, 15609c730a4SSundar Iyer .flags = IORESOURCE_IRQ, 15709c730a4SSundar Iyer }, 15809c730a4SSundar Iyer }; 15909c730a4SSundar Iyer 160afb580a9SGeert Uytterhoeven static const struct mfd_cell tc3589x_dev_gpio[] = { 161f4e8afdcSSundar Iyer { 16220406ebfSSundar Iyer .name = "tc3589x-gpio", 163f4e8afdcSSundar Iyer .num_resources = ARRAY_SIZE(gpio_resources), 164f4e8afdcSSundar Iyer .resources = &gpio_resources[0], 165*a381b13eSLinus Walleij .of_compatible = "toshiba,tc3589x-gpio", 166f4e8afdcSSundar Iyer }, 167f4e8afdcSSundar Iyer }; 168f4e8afdcSSundar Iyer 169afb580a9SGeert Uytterhoeven static const struct mfd_cell tc3589x_dev_keypad[] = { 17009c730a4SSundar Iyer { 17109c730a4SSundar Iyer .name = "tc3589x-keypad", 17209c730a4SSundar Iyer .num_resources = ARRAY_SIZE(keypad_resources), 17309c730a4SSundar Iyer .resources = &keypad_resources[0], 174*a381b13eSLinus Walleij .of_compatible = "toshiba,tc3589x-keypad", 17509c730a4SSundar Iyer }, 17609c730a4SSundar Iyer }; 17709c730a4SSundar Iyer 17820406ebfSSundar Iyer static irqreturn_t tc3589x_irq(int irq, void *data) 179f4e8afdcSSundar Iyer { 18020406ebfSSundar Iyer struct tc3589x *tc3589x = data; 181f4e8afdcSSundar Iyer int status; 182f4e8afdcSSundar Iyer 183bd77efd0SSundar Iyer again: 18420406ebfSSundar Iyer status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); 185f4e8afdcSSundar Iyer if (status < 0) 186f4e8afdcSSundar Iyer return IRQ_NONE; 187f4e8afdcSSundar Iyer 188f4e8afdcSSundar Iyer while (status) { 189f4e8afdcSSundar Iyer int bit = __ffs(status); 19015e27b10SLee Jones int virq = irq_create_mapping(tc3589x->domain, bit); 191f4e8afdcSSundar Iyer 19215e27b10SLee Jones handle_nested_irq(virq); 193f4e8afdcSSundar Iyer status &= ~(1 << bit); 194f4e8afdcSSundar Iyer } 195f4e8afdcSSundar Iyer 196f4e8afdcSSundar Iyer /* 197f4e8afdcSSundar Iyer * A dummy read or write (to any register) appears to be necessary to 198f4e8afdcSSundar Iyer * have the last interrupt clear (for example, GPIO IC write) take 199bd77efd0SSundar Iyer * effect. In such a case, recheck for any interrupt which is still 200bd77efd0SSundar Iyer * pending. 201f4e8afdcSSundar Iyer */ 202bd77efd0SSundar Iyer status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); 203bd77efd0SSundar Iyer if (status) 204bd77efd0SSundar Iyer goto again; 205f4e8afdcSSundar Iyer 206f4e8afdcSSundar Iyer return IRQ_HANDLED; 207f4e8afdcSSundar Iyer } 208f4e8afdcSSundar Iyer 20915e27b10SLee Jones static int tc3589x_irq_map(struct irq_domain *d, unsigned int virq, 21015e27b10SLee Jones irq_hw_number_t hwirq) 211f4e8afdcSSundar Iyer { 21215e27b10SLee Jones struct tc3589x *tc3589x = d->host_data; 213f4e8afdcSSundar Iyer 21415e27b10SLee Jones irq_set_chip_data(virq, tc3589x); 21515e27b10SLee Jones irq_set_chip_and_handler(virq, &dummy_irq_chip, 216f4e8afdcSSundar Iyer handle_edge_irq); 21715e27b10SLee Jones irq_set_nested_thread(virq, 1); 218f4e8afdcSSundar Iyer #ifdef CONFIG_ARM 21915e27b10SLee Jones set_irq_flags(virq, IRQF_VALID); 220f4e8afdcSSundar Iyer #else 22115e27b10SLee Jones irq_set_noprobe(virq); 222f4e8afdcSSundar Iyer #endif 223f4e8afdcSSundar Iyer 224f4e8afdcSSundar Iyer return 0; 225f4e8afdcSSundar Iyer } 226f4e8afdcSSundar Iyer 22715e27b10SLee Jones static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq) 22815e27b10SLee Jones { 22915e27b10SLee Jones #ifdef CONFIG_ARM 23015e27b10SLee Jones set_irq_flags(virq, 0); 23115e27b10SLee Jones #endif 23215e27b10SLee Jones irq_set_chip_and_handler(virq, NULL, NULL); 23315e27b10SLee Jones irq_set_chip_data(virq, NULL); 23415e27b10SLee Jones } 23515e27b10SLee Jones 23615e27b10SLee Jones static struct irq_domain_ops tc3589x_irq_ops = { 23715e27b10SLee Jones .map = tc3589x_irq_map, 23815e27b10SLee Jones .unmap = tc3589x_irq_unmap, 23915e27b10SLee Jones .xlate = irq_domain_xlate_twocell, 24015e27b10SLee Jones }; 24115e27b10SLee Jones 242a435ae1dSLee Jones static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np) 243f4e8afdcSSundar Iyer { 24420406ebfSSundar Iyer int base = tc3589x->irq_base; 245f4e8afdcSSundar Iyer 2461f0529b4SLinus Walleij tc3589x->domain = irq_domain_add_simple( 2471f0529b4SLinus Walleij np, TC3589x_NR_INTERNAL_IRQS, base, 24815e27b10SLee Jones &tc3589x_irq_ops, tc3589x); 24915e27b10SLee Jones 25015e27b10SLee Jones if (!tc3589x->domain) { 25115e27b10SLee Jones dev_err(tc3589x->dev, "Failed to create irqdomain\n"); 25215e27b10SLee Jones return -ENOSYS; 25315e27b10SLee Jones } 25415e27b10SLee Jones 25515e27b10SLee Jones return 0; 256f4e8afdcSSundar Iyer } 257f4e8afdcSSundar Iyer 25820406ebfSSundar Iyer static int tc3589x_chip_init(struct tc3589x *tc3589x) 259f4e8afdcSSundar Iyer { 260f4e8afdcSSundar Iyer int manf, ver, ret; 261f4e8afdcSSundar Iyer 26220406ebfSSundar Iyer manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE); 263f4e8afdcSSundar Iyer if (manf < 0) 264f4e8afdcSSundar Iyer return manf; 265f4e8afdcSSundar Iyer 26620406ebfSSundar Iyer ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION); 267f4e8afdcSSundar Iyer if (ver < 0) 268f4e8afdcSSundar Iyer return ver; 269f4e8afdcSSundar Iyer 27020406ebfSSundar Iyer if (manf != TC3589x_MANFCODE_MAGIC) { 27120406ebfSSundar Iyer dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf); 272f4e8afdcSSundar Iyer return -EINVAL; 273f4e8afdcSSundar Iyer } 274f4e8afdcSSundar Iyer 27520406ebfSSundar Iyer dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver); 276f4e8afdcSSundar Iyer 277523bc382SSundar Iyer /* 278523bc382SSundar Iyer * Put everything except the IRQ module into reset; 279523bc382SSundar Iyer * also spare the GPIO module for any pin initialization 280523bc382SSundar Iyer * done during pre-kernel boot 281523bc382SSundar Iyer */ 28220406ebfSSundar Iyer ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL, 28320406ebfSSundar Iyer TC3589x_RSTCTRL_TIMRST 28420406ebfSSundar Iyer | TC3589x_RSTCTRL_ROTRST 285523bc382SSundar Iyer | TC3589x_RSTCTRL_KBDRST); 286f4e8afdcSSundar Iyer if (ret < 0) 287f4e8afdcSSundar Iyer return ret; 288f4e8afdcSSundar Iyer 289f4e8afdcSSundar Iyer /* Clear the reset interrupt. */ 29020406ebfSSundar Iyer return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); 291f4e8afdcSSundar Iyer } 292f4e8afdcSSundar Iyer 293f791be49SBill Pemberton static int tc3589x_device_init(struct tc3589x *tc3589x) 294611b7590SSundar Iyer { 295611b7590SSundar Iyer int ret = 0; 296611b7590SSundar Iyer unsigned int blocks = tc3589x->pdata->block; 297611b7590SSundar Iyer 298611b7590SSundar Iyer if (blocks & TC3589x_BLOCK_GPIO) { 299611b7590SSundar Iyer ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, 300611b7590SSundar Iyer ARRAY_SIZE(tc3589x_dev_gpio), NULL, 30115e27b10SLee Jones tc3589x->irq_base, tc3589x->domain); 302611b7590SSundar Iyer if (ret) { 303611b7590SSundar Iyer dev_err(tc3589x->dev, "failed to add gpio child\n"); 304611b7590SSundar Iyer return ret; 305611b7590SSundar Iyer } 306611b7590SSundar Iyer dev_info(tc3589x->dev, "added gpio block\n"); 307611b7590SSundar Iyer } 308611b7590SSundar Iyer 30909c730a4SSundar Iyer if (blocks & TC3589x_BLOCK_KEYPAD) { 31009c730a4SSundar Iyer ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, 31109c730a4SSundar Iyer ARRAY_SIZE(tc3589x_dev_keypad), NULL, 31215e27b10SLee Jones tc3589x->irq_base, tc3589x->domain); 31309c730a4SSundar Iyer if (ret) { 31409c730a4SSundar Iyer dev_err(tc3589x->dev, "failed to keypad child\n"); 315611b7590SSundar Iyer return ret; 31609c730a4SSundar Iyer } 31709c730a4SSundar Iyer dev_info(tc3589x->dev, "added keypad block\n"); 31809c730a4SSundar Iyer } 319611b7590SSundar Iyer 32009c730a4SSundar Iyer return ret; 321611b7590SSundar Iyer } 322611b7590SSundar Iyer 323*a381b13eSLinus Walleij #ifdef CONFIG_OF 324*a381b13eSLinus Walleij static const struct of_device_id tc3589x_match[] = { 325*a381b13eSLinus Walleij /* Legacy compatible string */ 326*a381b13eSLinus Walleij { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN }, 327*a381b13eSLinus Walleij { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 }, 328*a381b13eSLinus Walleij { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 }, 329*a381b13eSLinus Walleij { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 }, 330*a381b13eSLinus Walleij { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 }, 331*a381b13eSLinus Walleij { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 }, 332*a381b13eSLinus Walleij { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 }, 333*a381b13eSLinus Walleij { } 334*a381b13eSLinus Walleij }; 335*a381b13eSLinus Walleij 336*a381b13eSLinus Walleij MODULE_DEVICE_TABLE(of, tc3589x_match); 337*a381b13eSLinus Walleij 338*a381b13eSLinus Walleij static struct tc3589x_platform_data * 339*a381b13eSLinus Walleij tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) 340a435ae1dSLee Jones { 341*a381b13eSLinus Walleij struct device_node *np = dev->of_node; 342*a381b13eSLinus Walleij struct tc3589x_platform_data *pdata; 343a435ae1dSLee Jones struct device_node *child; 344*a381b13eSLinus Walleij const struct of_device_id *of_id; 345*a381b13eSLinus Walleij 346*a381b13eSLinus Walleij pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 347*a381b13eSLinus Walleij if (!pdata) 348*a381b13eSLinus Walleij return ERR_PTR(-ENOMEM); 349*a381b13eSLinus Walleij 350*a381b13eSLinus Walleij of_id = of_match_device(tc3589x_match, dev); 351*a381b13eSLinus Walleij if (!of_id) 352*a381b13eSLinus Walleij return ERR_PTR(-ENODEV); 353*a381b13eSLinus Walleij *version = (enum tc3589x_version) of_id->data; 354a435ae1dSLee Jones 355a435ae1dSLee Jones for_each_child_of_node(np, child) { 356*a381b13eSLinus Walleij if (of_device_is_compatible(child, "toshiba,tc3589x-gpio")) 357a435ae1dSLee Jones pdata->block |= TC3589x_BLOCK_GPIO; 358*a381b13eSLinus Walleij if (of_device_is_compatible(child, "toshiba,tc3589x-keypad")) 359a435ae1dSLee Jones pdata->block |= TC3589x_BLOCK_KEYPAD; 360a435ae1dSLee Jones } 361a435ae1dSLee Jones 362*a381b13eSLinus Walleij return pdata; 363a435ae1dSLee Jones } 364*a381b13eSLinus Walleij #else 365*a381b13eSLinus Walleij static inline struct tc3589x_platform_data * 366*a381b13eSLinus Walleij tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) 367*a381b13eSLinus Walleij { 368*a381b13eSLinus Walleij dev_err(dev, "no device tree support\n"); 369*a381b13eSLinus Walleij return ERR_PTR(-ENODEV); 370*a381b13eSLinus Walleij } 371*a381b13eSLinus Walleij #endif 372a435ae1dSLee Jones 373f791be49SBill Pemberton static int tc3589x_probe(struct i2c_client *i2c, 374f4e8afdcSSundar Iyer const struct i2c_device_id *id) 375f4e8afdcSSundar Iyer { 376a435ae1dSLee Jones struct device_node *np = i2c->dev.of_node; 377*a381b13eSLinus Walleij struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev); 37820406ebfSSundar Iyer struct tc3589x *tc3589x; 379*a381b13eSLinus Walleij enum tc3589x_version version; 380f4e8afdcSSundar Iyer int ret; 381f4e8afdcSSundar Iyer 382a435ae1dSLee Jones if (!pdata) { 383*a381b13eSLinus Walleij pdata = tc3589x_of_probe(&i2c->dev, &version); 384*a381b13eSLinus Walleij if (IS_ERR(pdata)) { 385a435ae1dSLee Jones dev_err(&i2c->dev, "No platform data or DT found\n"); 386*a381b13eSLinus Walleij return PTR_ERR(pdata); 387a435ae1dSLee Jones } 388*a381b13eSLinus Walleij } else { 389*a381b13eSLinus Walleij /* When not probing from device tree we have this ID */ 390*a381b13eSLinus Walleij version = id->driver_data; 391a435ae1dSLee Jones } 392a435ae1dSLee Jones 393f4e8afdcSSundar Iyer if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA 394f4e8afdcSSundar Iyer | I2C_FUNC_SMBUS_I2C_BLOCK)) 395f4e8afdcSSundar Iyer return -EIO; 396f4e8afdcSSundar Iyer 3971383e00fSJingoo Han tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x), 3981383e00fSJingoo Han GFP_KERNEL); 39920406ebfSSundar Iyer if (!tc3589x) 400f4e8afdcSSundar Iyer return -ENOMEM; 401f4e8afdcSSundar Iyer 40220406ebfSSundar Iyer mutex_init(&tc3589x->lock); 403f4e8afdcSSundar Iyer 40420406ebfSSundar Iyer tc3589x->dev = &i2c->dev; 40520406ebfSSundar Iyer tc3589x->i2c = i2c; 40620406ebfSSundar Iyer tc3589x->pdata = pdata; 40720406ebfSSundar Iyer tc3589x->irq_base = pdata->irq_base; 408e64c1eb4SLinus Walleij 409*a381b13eSLinus Walleij switch (version) { 410e64c1eb4SLinus Walleij case TC3589X_TC35893: 411e64c1eb4SLinus Walleij case TC3589X_TC35895: 412e64c1eb4SLinus Walleij case TC3589X_TC35896: 413e64c1eb4SLinus Walleij tc3589x->num_gpio = 20; 414e64c1eb4SLinus Walleij break; 415e64c1eb4SLinus Walleij case TC3589X_TC35890: 416e64c1eb4SLinus Walleij case TC3589X_TC35892: 417e64c1eb4SLinus Walleij case TC3589X_TC35894: 418e64c1eb4SLinus Walleij case TC3589X_UNKNOWN: 419e64c1eb4SLinus Walleij default: 420e64c1eb4SLinus Walleij tc3589x->num_gpio = 24; 421e64c1eb4SLinus Walleij break; 422e64c1eb4SLinus Walleij } 423f4e8afdcSSundar Iyer 42420406ebfSSundar Iyer i2c_set_clientdata(i2c, tc3589x); 425f4e8afdcSSundar Iyer 42620406ebfSSundar Iyer ret = tc3589x_chip_init(tc3589x); 427f4e8afdcSSundar Iyer if (ret) 4281383e00fSJingoo Han return ret; 429f4e8afdcSSundar Iyer 430a435ae1dSLee Jones ret = tc3589x_irq_init(tc3589x, np); 431f4e8afdcSSundar Iyer if (ret) 4321383e00fSJingoo Han return ret; 433f4e8afdcSSundar Iyer 43420406ebfSSundar Iyer ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, 435f4e8afdcSSundar Iyer IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 43620406ebfSSundar Iyer "tc3589x", tc3589x); 437f4e8afdcSSundar Iyer if (ret) { 43820406ebfSSundar Iyer dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); 4391383e00fSJingoo Han return ret; 440f4e8afdcSSundar Iyer } 441f4e8afdcSSundar Iyer 442611b7590SSundar Iyer ret = tc3589x_device_init(tc3589x); 443f4e8afdcSSundar Iyer if (ret) { 444611b7590SSundar Iyer dev_err(tc3589x->dev, "failed to add child devices\n"); 4451383e00fSJingoo Han return ret; 446f4e8afdcSSundar Iyer } 447f4e8afdcSSundar Iyer 448f4e8afdcSSundar Iyer return 0; 449f4e8afdcSSundar Iyer } 450f4e8afdcSSundar Iyer 4514740f73fSBill Pemberton static int tc3589x_remove(struct i2c_client *client) 452f4e8afdcSSundar Iyer { 45320406ebfSSundar Iyer struct tc3589x *tc3589x = i2c_get_clientdata(client); 454f4e8afdcSSundar Iyer 45520406ebfSSundar Iyer mfd_remove_devices(tc3589x->dev); 456f4e8afdcSSundar Iyer 457f4e8afdcSSundar Iyer return 0; 458f4e8afdcSSundar Iyer } 459f4e8afdcSSundar Iyer 460930bf022SAxel Lin #ifdef CONFIG_PM_SLEEP 461593e9d70SSundar Iyer static int tc3589x_suspend(struct device *dev) 462593e9d70SSundar Iyer { 463593e9d70SSundar Iyer struct tc3589x *tc3589x = dev_get_drvdata(dev); 464593e9d70SSundar Iyer struct i2c_client *client = tc3589x->i2c; 465593e9d70SSundar Iyer int ret = 0; 466593e9d70SSundar Iyer 467593e9d70SSundar Iyer /* put the system to sleep mode */ 468593e9d70SSundar Iyer if (!device_may_wakeup(&client->dev)) 469593e9d70SSundar Iyer ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, 470593e9d70SSundar Iyer TC3589x_CLKMODE_MODCTL_SLEEP); 471593e9d70SSundar Iyer 472593e9d70SSundar Iyer return ret; 473593e9d70SSundar Iyer } 474593e9d70SSundar Iyer 475593e9d70SSundar Iyer static int tc3589x_resume(struct device *dev) 476593e9d70SSundar Iyer { 477593e9d70SSundar Iyer struct tc3589x *tc3589x = dev_get_drvdata(dev); 478593e9d70SSundar Iyer struct i2c_client *client = tc3589x->i2c; 479593e9d70SSundar Iyer int ret = 0; 480593e9d70SSundar Iyer 481593e9d70SSundar Iyer /* enable the system into operation */ 482593e9d70SSundar Iyer if (!device_may_wakeup(&client->dev)) 483593e9d70SSundar Iyer ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, 484593e9d70SSundar Iyer TC3589x_CLKMODE_MODCTL_OPERATION); 485593e9d70SSundar Iyer 486593e9d70SSundar Iyer return ret; 487593e9d70SSundar Iyer } 48854d8e2c3SLinus Walleij #endif 489593e9d70SSundar Iyer 490930bf022SAxel Lin static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume); 491930bf022SAxel Lin 49220406ebfSSundar Iyer static const struct i2c_device_id tc3589x_id[] = { 493e64c1eb4SLinus Walleij { "tc35890", TC3589X_TC35890 }, 494e64c1eb4SLinus Walleij { "tc35892", TC3589X_TC35892 }, 495e64c1eb4SLinus Walleij { "tc35893", TC3589X_TC35893 }, 496e64c1eb4SLinus Walleij { "tc35894", TC3589X_TC35894 }, 497e64c1eb4SLinus Walleij { "tc35895", TC3589X_TC35895 }, 498e64c1eb4SLinus Walleij { "tc35896", TC3589X_TC35896 }, 499e64c1eb4SLinus Walleij { "tc3589x", TC3589X_UNKNOWN }, 500f4e8afdcSSundar Iyer { } 501f4e8afdcSSundar Iyer }; 50220406ebfSSundar Iyer MODULE_DEVICE_TABLE(i2c, tc3589x_id); 503f4e8afdcSSundar Iyer 50420406ebfSSundar Iyer static struct i2c_driver tc3589x_driver = { 505*a381b13eSLinus Walleij .driver = { 506*a381b13eSLinus Walleij .name = "tc3589x", 507*a381b13eSLinus Walleij .owner = THIS_MODULE, 508*a381b13eSLinus Walleij .pm = &tc3589x_dev_pm_ops, 509*a381b13eSLinus Walleij .of_match_table = of_match_ptr(tc3589x_match), 510*a381b13eSLinus Walleij }, 51120406ebfSSundar Iyer .probe = tc3589x_probe, 51284449216SBill Pemberton .remove = tc3589x_remove, 51320406ebfSSundar Iyer .id_table = tc3589x_id, 514f4e8afdcSSundar Iyer }; 515f4e8afdcSSundar Iyer 51620406ebfSSundar Iyer static int __init tc3589x_init(void) 517f4e8afdcSSundar Iyer { 51820406ebfSSundar Iyer return i2c_add_driver(&tc3589x_driver); 519f4e8afdcSSundar Iyer } 52020406ebfSSundar Iyer subsys_initcall(tc3589x_init); 521f4e8afdcSSundar Iyer 52220406ebfSSundar Iyer static void __exit tc3589x_exit(void) 523f4e8afdcSSundar Iyer { 52420406ebfSSundar Iyer i2c_del_driver(&tc3589x_driver); 525f4e8afdcSSundar Iyer } 52620406ebfSSundar Iyer module_exit(tc3589x_exit); 527f4e8afdcSSundar Iyer 528f4e8afdcSSundar Iyer MODULE_LICENSE("GPL v2"); 52920406ebfSSundar Iyer MODULE_DESCRIPTION("TC3589x MFD core driver"); 530f4e8afdcSSundar Iyer MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); 531