diff options
Diffstat (limited to 'drivers/mfd/ab8500-core.c')
-rw-r--r-- | drivers/mfd/ab8500-core.c | 375 |
1 files changed, 285 insertions, 90 deletions
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index d295941c9a3d..1f08704f7ae8 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -32,6 +32,7 @@ #define AB8500_IT_SOURCE6_REG 0x05 #define AB8500_IT_SOURCE7_REG 0x06 #define AB8500_IT_SOURCE8_REG 0x07 +#define AB9540_IT_SOURCE13_REG 0x0C #define AB8500_IT_SOURCE19_REG 0x12 #define AB8500_IT_SOURCE20_REG 0x13 #define AB8500_IT_SOURCE21_REG 0x14 @@ -53,6 +54,7 @@ #define AB8500_IT_LATCH9_REG 0x28 #define AB8500_IT_LATCH10_REG 0x29 #define AB8500_IT_LATCH12_REG 0x2B +#define AB9540_IT_LATCH13_REG 0x2C #define AB8500_IT_LATCH19_REG 0x32 #define AB8500_IT_LATCH20_REG 0x33 #define AB8500_IT_LATCH21_REG 0x34 @@ -90,21 +92,39 @@ #define AB8500_IT_MASK24_REG 0x57 #define AB8500_REV_REG 0x80 +#define AB8500_IC_NAME_REG 0x82 #define AB8500_SWITCH_OFF_STATUS 0x00 #define AB8500_TURN_ON_STATUS 0x00 +#define AB9540_MODEM_CTRL2_REG 0x23 +#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2) + /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt - * numbers are indexed into this array with (num / 8). + * numbers are indexed into this array with (num / 8). The interupts are + * defined in linux/mfd/ab8500.h * * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at * offset 0. */ +/* AB8500 support */ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, }; +/* AB9540 support */ +static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, +}; + +static const char ab8500_version_str[][7] = { + [AB8500_VERSION_AB8500] = "AB8500", + [AB8500_VERSION_AB8505] = "AB8505", + [AB8500_VERSION_AB9540] = "AB9540", + [AB8500_VERSION_AB8540] = "AB8540", +}; + static int ab8500_get_chip_id(struct device *dev) { struct ab8500 *ab8500; @@ -127,9 +147,7 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank, dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data); - ret = mutex_lock_interruptible(&ab8500->lock); - if (ret) - return ret; + mutex_lock(&ab8500->lock); ret = ab8500->write(ab8500, addr, data); if (ret < 0) @@ -156,9 +174,7 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, * bank on higher 8 bits and reg in lower */ u16 addr = ((u16)bank) << 8 | reg; - ret = mutex_lock_interruptible(&ab8500->lock); - if (ret) - return ret; + mutex_lock(&ab8500->lock); ret = ab8500->read(ab8500, addr); if (ret < 0) @@ -185,31 +201,38 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { int ret; - u8 data; /* put the u8 bank and u8 reg together into a an u16. * bank on higher 8 bits and reg in lower */ u16 addr = ((u16)bank) << 8 | reg; - ret = mutex_lock_interruptible(&ab8500->lock); - if (ret) - return ret; + mutex_lock(&ab8500->lock); - ret = ab8500->read(ab8500, addr); - if (ret < 0) { - dev_err(ab8500->dev, "failed to read reg %#x: %d\n", - addr, ret); - goto out; - } + if (ab8500->write_masked == NULL) { + u8 data; - data = (u8)ret; - data = (~bitmask & data) | (bitmask & bitvalues); + ret = ab8500->read(ab8500, addr); + if (ret < 0) { + dev_err(ab8500->dev, "failed to read reg %#x: %d\n", + addr, ret); + goto out; + } - ret = ab8500->write(ab8500, addr, data); - if (ret < 0) - dev_err(ab8500->dev, "failed to write reg %#x: %d\n", - addr, ret); + data = (u8)ret; + data = (~bitmask & data) | (bitmask & bitvalues); + + ret = ab8500->write(ab8500, addr, data); + if (ret < 0) + dev_err(ab8500->dev, "failed to write reg %#x: %d\n", + addr, ret); - dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data); + dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, + data); + goto out; + } + ret = ab8500->write_masked(ab8500, addr, bitmask, bitvalues); + if (ret < 0) + dev_err(ab8500->dev, "failed to modify reg %#x: %d\n", addr, + ret); out: mutex_unlock(&ab8500->lock); return ret; @@ -248,7 +271,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); int i; - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + for (i = 0; i < ab8500->mask_size; i++) { u8 old = ab8500->oldmask[i]; u8 new = ab8500->mask[i]; int reg; @@ -256,14 +279,17 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) if (new == old) continue; - /* Interrupt register 12 doesn't exist prior to version 2.0 */ - if (ab8500_irq_regoffset[i] == 11 && - ab8500->chip_id < AB8500_CUT2P0) + /* + * Interrupt register 12 doesn't exist prior to AB8500 version + * 2.0 + */ + if (ab8500->irq_reg_offset[i] == 11 && + is_ab8500_1p1_or_earlier(ab8500)) continue; ab8500->oldmask[i] = new; - reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; + reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i]; set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new); } @@ -306,13 +332,16 @@ static irqreturn_t ab8500_irq(int irq, void *dev) dev_vdbg(ab8500->dev, "interrupt\n"); - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { - int regoffset = ab8500_irq_regoffset[i]; + for (i = 0; i < ab8500->mask_size; i++) { + int regoffset = ab8500->irq_reg_offset[i]; int status; u8 value; - /* Interrupt register 12 doesn't exist prior to version 2.0 */ - if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0) + /* + * Interrupt register 12 doesn't exist prior to AB8500 version + * 2.0 + */ + if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500)) continue; status = get_register_interruptible(ab8500, AB8500_INTERRUPT, @@ -336,8 +365,16 @@ static int ab8500_irq_init(struct ab8500 *ab8500) { int base = ab8500->irq_base; int irq; + int num_irqs; + + if (is_ab9540(ab8500)) + num_irqs = AB9540_NR_IRQS; + else if (is_ab8505(ab8500)) + num_irqs = AB8505_NR_IRQS; + else + num_irqs = AB8500_NR_IRQS; - for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { + for (irq = base; irq < base + num_irqs; irq++) { irq_set_chip_data(irq, ab8500); irq_set_chip_and_handler(irq, &ab8500_irq_chip, handle_simple_irq); @@ -356,8 +393,16 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) { int base = ab8500->irq_base; int irq; + int num_irqs; - for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { + if (is_ab9540(ab8500)) + num_irqs = AB9540_NR_IRQS; + else if (is_ab8505(ab8500)) + num_irqs = AB8505_NR_IRQS; + else + num_irqs = AB8500_NR_IRQS; + + for (irq = base; irq < base + num_irqs; irq++) { #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif @@ -366,6 +411,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) } } +/* AB8500 GPIO Resources */ static struct resource __devinitdata ab8500_gpio_resources[] = { { .name = "GPIO_INT6", @@ -375,6 +421,28 @@ static struct resource __devinitdata ab8500_gpio_resources[] = { } }; +/* AB9540 GPIO Resources */ +static struct resource __devinitdata ab9540_gpio_resources[] = { + { + .name = "GPIO_INT6", + .start = AB8500_INT_GPIO6R, + .end = AB8500_INT_GPIO41F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "GPIO_INT14", + .start = AB9540_INT_GPIO50R, + .end = AB9540_INT_GPIO54R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "GPIO_INT15", + .start = AB9540_INT_GPIO50F, + .end = AB9540_INT_GPIO54F, + .flags = IORESOURCE_IRQ, + } +}; + static struct resource __devinitdata ab8500_gpadc_resources[] = { { .name = "HW_CONV_END", @@ -491,12 +559,6 @@ static struct resource __devinitdata ab8500_charger_resources[] = { .flags = IORESOURCE_IRQ, }, { - .name = "USB_CHARGE_DET_DONE", - .start = AB8500_INT_USB_CHG_DET_DONE, - .end = AB8500_INT_USB_CHG_DET_DONE, - .flags = IORESOURCE_IRQ, - }, - { .name = "VBUS_OVV", .start = AB8500_INT_VBUS_OVV, .end = AB8500_INT_VBUS_OVV, @@ -534,14 +596,8 @@ static struct resource __devinitdata ab8500_charger_resources[] = { }, { .name = "USB_CHARGER_NOT_OKR", - .start = AB8500_INT_USB_CHARGER_NOT_OK, - .end = AB8500_INT_USB_CHARGER_NOT_OK, - .flags = IORESOURCE_IRQ, - }, - { - .name = "USB_CHARGER_NOT_OKF", - .start = AB8500_INT_USB_CHARGER_NOT_OKF, - .end = AB8500_INT_USB_CHARGER_NOT_OKF, + .start = AB8500_INT_USB_CHARGER_NOT_OKR, + .end = AB8500_INT_USB_CHARGER_NOT_OKR, .flags = IORESOURCE_IRQ, }, { @@ -616,6 +672,12 @@ static struct resource __devinitdata ab8500_fg_resources[] = { .end = AB8500_INT_CC_INT_CALIB, .flags = IORESOURCE_IRQ, }, + { + .name = "CCEOC", + .start = AB8500_INT_CCEOC, + .end = AB8500_INT_CCEOC, + .flags = IORESOURCE_IRQ, + }, }; static struct resource __devinitdata ab8500_chargalg_resources[] = {}; @@ -630,8 +692,8 @@ static struct resource __devinitdata ab8500_debug_resources[] = { }, { .name = "IRQ_LAST", - .start = AB8500_INT_USB_CHARGER_NOT_OKF, - .end = AB8500_INT_USB_CHARGER_NOT_OKF, + .start = AB8500_INT_XTAL32K_KO, + .end = AB8500_INT_XTAL32K_KO, .flags = IORESOURCE_IRQ, }, }; @@ -691,7 +753,7 @@ static struct resource __devinitdata ab8500_temp_resources[] = { }, }; -static struct mfd_cell __devinitdata ab8500_devs[] = { +static struct mfd_cell __devinitdata abx500_common_devs[] = { #ifdef CONFIG_DEBUG_FS { .name = "ab8500-debug", @@ -706,11 +768,6 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { .name = "ab8500-regulator", }, { - .name = "ab8500-gpio", - .num_resources = ARRAY_SIZE(ab8500_gpio_resources), - .resources = ab8500_gpio_resources, - }, - { .name = "ab8500-gpadc", .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), .resources = ab8500_gpadc_resources, @@ -748,11 +805,7 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { { .name = "ab8500-codec", }, - { - .name = "ab8500-usb", - .num_resources = ARRAY_SIZE(ab8500_usb_resources), - .resources = ab8500_usb_resources, - }, + { .name = "ab8500-poweron-key", .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), @@ -781,6 +834,32 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { }, }; +static struct mfd_cell __devinitdata ab8500_devs[] = { + { + .name = "ab8500-gpio", + .num_resources = ARRAY_SIZE(ab8500_gpio_resources), + .resources = ab8500_gpio_resources, + }, + { + .name = "ab8500-usb", + .num_resources = ARRAY_SIZE(ab8500_usb_resources), + .resources = ab8500_usb_resources, + }, +}; + +static struct mfd_cell __devinitdata ab9540_devs[] = { + { + .name = "ab8500-gpio", + .num_resources = ARRAY_SIZE(ab9540_gpio_resources), + .resources = ab9540_gpio_resources, + }, + { + .name = "ab9540-usb", + .num_resources = ARRAY_SIZE(ab8500_usb_resources), + .resources = ab8500_usb_resources, + }, +}; + static ssize_t show_chip_id(struct device *dev, struct device_attribute *attr, char *buf) { @@ -842,9 +921,64 @@ static ssize_t show_turn_on_status(struct device *dev, return sprintf(buf, "%#x\n", value); } +static ssize_t show_ab9540_dbbrstn(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ab8500 *ab8500; + int ret; + u8 value; + + ab8500 = dev_get_drvdata(dev); + + ret = get_register_interruptible(ab8500, AB8500_REGU_CTRL2, + AB9540_MODEM_CTRL2_REG, &value); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", + (value & AB9540_MODEM_CTRL2_SWDBBRSTN_BIT) ? 1 : 0); +} + +static ssize_t store_ab9540_dbbrstn(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ab8500 *ab8500; + int ret = count; + int err; + u8 bitvalues; + + ab8500 = dev_get_drvdata(dev); + + if (count > 0) { + switch (buf[0]) { + case '0': + bitvalues = 0; + break; + case '1': + bitvalues = AB9540_MODEM_CTRL2_SWDBBRSTN_BIT; + break; + default: + goto exit; + } + + err = mask_and_set_register_interruptible(ab8500, + AB8500_REGU_CTRL2, AB9540_MODEM_CTRL2_REG, + AB9540_MODEM_CTRL2_SWDBBRSTN_BIT, bitvalues); + if (err) + dev_info(ab8500->dev, + "Failed to set DBBRSTN %c, err %#x\n", + buf[0], err); + } + +exit: + return ret; +} + static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL); +static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR, + show_ab9540_dbbrstn, store_ab9540_dbbrstn); static struct attribute *ab8500_sysfs_entries[] = { &dev_attr_chip_id.attr, @@ -853,11 +987,23 @@ static struct attribute *ab8500_sysfs_entries[] = { NULL, }; +static struct attribute *ab9540_sysfs_entries[] = { + &dev_attr_chip_id.attr, + &dev_attr_switch_off_status.attr, + &dev_attr_turn_on_status.attr, + &dev_attr_dbbrstn.attr, + NULL, +}; + static struct attribute_group ab8500_attr_group = { .attrs = ab8500_sysfs_entries, }; -int __devinit ab8500_init(struct ab8500 *ab8500) +static struct attribute_group ab9540_attr_group = { + .attrs = ab9540_sysfs_entries, +}; + +int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) { struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); int ret; @@ -870,25 +1016,45 @@ int __devinit ab8500_init(struct ab8500 *ab8500) mutex_init(&ab8500->lock); mutex_init(&ab8500->irq_lock); + if (version != AB8500_VERSION_UNDEFINED) + ab8500->version = version; + else { + ret = get_register_interruptible(ab8500, AB8500_MISC, + AB8500_IC_NAME_REG, &value); + if (ret < 0) + return ret; + + ab8500->version = value; + } + ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_REV_REG, &value); if (ret < 0) return ret; - switch (value) { - case AB8500_CUT1P0: - case AB8500_CUT1P1: - case AB8500_CUT2P0: - case AB8500_CUT3P0: - case AB8500_CUT3P3: - dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); - break; - default: - dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); - return -EINVAL; - } ab8500->chip_id = value; + dev_info(ab8500->dev, "detected chip, %s rev. %1x.%1x\n", + ab8500_version_str[ab8500->version], + ab8500->chip_id >> 4, + ab8500->chip_id & 0x0F); + + /* Configure AB8500 or AB9540 IRQ */ + if (is_ab9540(ab8500) || is_ab8505(ab8500)) { + ab8500->mask_size = AB9540_NUM_IRQ_REGS; + ab8500->irq_reg_offset = ab9540_irq_regoffset; + } else { + ab8500->mask_size = AB8500_NUM_IRQ_REGS; + ab8500->irq_reg_offset = ab8500_irq_regoffset; + } + ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL); + if (!ab8500->mask) + return -ENOMEM; + ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL); + if (!ab8500->oldmask) { + ret = -ENOMEM; + goto out_freemask; + } /* * ab8500 has switched off due to (SWITCH_OFF_STATUS): * 0x01 Swoff bit programming @@ -911,30 +1077,33 @@ int __devinit ab8500_init(struct ab8500 *ab8500) plat->init(ab8500); /* Clear and mask all interrupts */ - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { - /* Interrupt register 12 doesn't exist prior to version 2.0 */ - if (ab8500_irq_regoffset[i] == 11 && - ab8500->chip_id < AB8500_CUT2P0) + for (i = 0; i < ab8500->mask_size; i++) { + /* + * Interrupt register 12 doesn't exist prior to AB8500 version + * 2.0 + */ + if (ab8500->irq_reg_offset[i] == 11 && + is_ab8500_1p1_or_earlier(ab8500)) continue; get_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_LATCH1_REG + ab8500_irq_regoffset[i], + AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i], &value); set_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i], 0xff); + AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i], 0xff); } ret = abx500_register_ops(ab8500->dev, &ab8500_ops); if (ret) - return ret; + goto out_freeoldmask; - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) + for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; if (ab8500->irq_base) { ret = ab8500_irq_init(ab8500); if (ret) - return ret; + goto out_freeoldmask; ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, @@ -943,17 +1112,34 @@ int __devinit ab8500_init(struct ab8500 *ab8500) goto out_removeirq; } - ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, - ARRAY_SIZE(ab8500_devs), NULL, + ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, + ARRAY_SIZE(abx500_common_devs), NULL, ab8500->irq_base); + if (ret) goto out_freeirq; - ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group); + if (is_ab9540(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, + ab8500->irq_base); + else + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab9540_devs), NULL, + ab8500->irq_base); if (ret) - dev_err(ab8500->dev, "error creating sysfs entries\n"); + goto out_freeirq; - return ret; + if (is_ab9540(ab8500)) + ret = sysfs_create_group(&ab8500->dev->kobj, + &ab9540_attr_group); + else + ret = sysfs_create_group(&ab8500->dev->kobj, + &ab8500_attr_group); + if (ret) + dev_err(ab8500->dev, "error creating sysfs entries\n"); + else + return ret; out_freeirq: if (ab8500->irq_base) @@ -961,18 +1147,27 @@ out_freeirq: out_removeirq: if (ab8500->irq_base) ab8500_irq_remove(ab8500); +out_freeoldmask: + kfree(ab8500->oldmask); +out_freemask: + kfree(ab8500->mask); return ret; } int __devexit ab8500_exit(struct ab8500 *ab8500) { - sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); + if (is_ab9540(ab8500)) + sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); + else + sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); mfd_remove_devices(ab8500->dev); if (ab8500->irq_base) { free_irq(ab8500->irq, ab8500); ab8500_irq_remove(ab8500); } + kfree(ab8500->oldmask); + kfree(ab8500->mask); return 0; } |