Merge branch 'pm-qos'
[firefly-linux-kernel-4.4.55.git] / drivers / mfd / ab8500-core.c
index dac0e299860353f0a299a8b66c14c745e587a133..47adf800024e01f8cdf05856eb3b356226c527f3 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -140,7 +141,7 @@ static const char ab8500_version_str[][7] = {
        [AB8500_VERSION_AB8540] = "AB8540",
 };
 
-static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_prcmu_write(struct ab8500 *ab8500, u16 addr, u8 data)
 {
        int ret;
 
@@ -150,7 +151,7 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
        return ret;
 }
 
-static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
+static int ab8500_prcmu_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
        u8 data)
 {
        int ret;
@@ -162,7 +163,7 @@ static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
        return ret;
 }
 
-static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+static int ab8500_prcmu_read(struct ab8500 *ab8500, u16 addr)
 {
        int ret;
        u8 data;
@@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
 static void ab8500_irq_mask(struct irq_data *data)
 {
        struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
-       int offset = data->irq - ab8500->irq_base;
+       int offset = data->hwirq;
        int index = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data)
 static void ab8500_irq_unmask(struct irq_data *data)
 {
        struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
-       int offset = data->irq - ab8500->irq_base;
+       int offset = data->hwirq;
        int index = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-static int ab8500_irq_init(struct ab8500 *ab8500)
+/**
+ * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
+ *
+ * @ab8500: ab8500_irq controller to operate on.
+ * @irq: index of the interrupt requested in the chip IRQs
+ *
+ * Useful for drivers to request their own IRQs.
+ */
+int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
 {
-       int base = ab8500->irq_base;
-       int irq;
-       int num_irqs;
+       if (!ab8500)
+               return -EINVAL;
 
-       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;
+       return irq_create_mapping(ab8500->domain, irq);
+}
+EXPORT_SYMBOL_GPL(ab8500_irq_get_virq);
+
+static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
+                               irq_hw_number_t hwirq)
+{
+       struct ab8500 *ab8500 = d->host_data;
+
+       if (!ab8500)
+               return -EINVAL;
 
-       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);
-               irq_set_nested_thread(irq, 1);
+       irq_set_chip_data(virq, ab8500);
+       irq_set_chip_and_handler(virq, &ab8500_irq_chip,
+                               handle_simple_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 ab8500_irq_remove(struct ab8500 *ab8500)
+static struct irq_domain_ops ab8500_irq_ops = {
+        .map    = ab8500_irq_map,
+        .xlate  = irq_domain_xlate_twocell,
+};
+
+static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
 {
-       int base = ab8500->irq_base;
-       int irq;
        int num_irqs;
 
        if (is_ab9540(ab8500))
@@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
        else
                num_irqs = AB8500_NR_IRQS;
 
-       for (irq = base; irq < base + num_irqs; irq++) {
-#ifdef CONFIG_ARM
-               set_irq_flags(irq, 0);
-#endif
-               irq_set_chip_and_handler(irq, NULL, NULL);
-               irq_set_chip_data(irq, NULL);
+       if (ab8500->irq_base) {
+               ab8500->domain = irq_domain_add_legacy(
+                       NULL, num_irqs, ab8500->irq_base,
+                       0, &ab8500_irq_ops, ab8500);
+       }
+       else {
+               ab8500->domain = irq_domain_add_linear(
+                       np, num_irqs, &ab8500_irq_ops, ab8500);
        }
+
+       if (!ab8500->domain) {
+               dev_err(ab8500->dev, "Failed to create irqdomain\n");
+               return -ENOSYS;
+       }
+
+       return 0;
 }
 
 int ab8500_suspend(struct ab8500 *ab8500)
@@ -947,54 +970,69 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
 #ifdef CONFIG_DEBUG_FS
        {
                .name = "ab8500-debug",
+               .of_compatible = "stericsson,ab8500-debug",
                .num_resources = ARRAY_SIZE(ab8500_debug_resources),
                .resources = ab8500_debug_resources,
        },
 #endif
        {
                .name = "ab8500-sysctrl",
+               .of_compatible = "stericsson,ab8500-sysctrl",
        },
        {
                .name = "ab8500-regulator",
+               .of_compatible = "stericsson,ab8500-regulator",
        },
        {
                .name = "ab8500-gpadc",
+               .of_compatible = "stericsson,ab8500-gpadc",
                .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
                .resources = ab8500_gpadc_resources,
        },
        {
                .name = "ab8500-rtc",
+               .of_compatible = "stericsson,ab8500-rtc",
                .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
                .resources = ab8500_rtc_resources,
        },
        {
                .name = "ab8500-acc-det",
+               .of_compatible = "stericsson,ab8500-acc-det",
                .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
                .resources = ab8500_av_acc_detect_resources,
        },
        {
                .name = "ab8500-poweron-key",
+               .of_compatible = "stericsson,ab8500-poweron-key",
                .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
                .resources = ab8500_poweronkey_db_resources,
        },
        {
                .name = "ab8500-pwm",
+               .of_compatible = "stericsson,ab8500-pwm",
                .id = 1,
        },
        {
                .name = "ab8500-pwm",
+               .of_compatible = "stericsson,ab8500-pwm",
                .id = 2,
        },
        {
                .name = "ab8500-pwm",
+               .of_compatible = "stericsson,ab8500-pwm",
                .id = 3,
        },
-       { .name = "ab8500-leds", },
+       {
+               .name = "ab8500-leds",
+               .of_compatible = "stericsson,ab8500-leds",
+       },
        {
                .name = "ab8500-denc",
+               .of_compatible = "stericsson,ab8500-denc",
        },
        {
                .name = "ab8500-temp",
+               .of_compatible = "stericsson,ab8500-temp",
                .num_resources = ARRAY_SIZE(ab8500_temp_resources),
                .resources = ab8500_temp_resources,
        },
@@ -1026,11 +1064,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
 static struct mfd_cell __devinitdata ab8500_devs[] = {
        {
                .name = "ab8500-gpio",
+               .of_compatible = "stericsson,ab8500-gpio",
                .num_resources = ARRAY_SIZE(ab8500_gpio_resources),
                .resources = ab8500_gpio_resources,
        },
        {
                .name = "ab8500-usb",
+               .of_compatible = "stericsson,ab8500-usb",
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
                .resources = ab8500_usb_resources,
        },
@@ -1207,16 +1247,17 @@ static struct attribute_group ab9540_attr_group = {
        .attrs  = ab9540_sysfs_entries,
 };
 
-static const struct of_device_id ab8500_match[] = {
-       {
-               .compatible = "stericsson,ab8500",
-               .data = (void *)AB8500_VERSION_AB8500,
-       },
-       {},
-};
-
 static int __devinit ab8500_probe(struct platform_device *pdev)
 {
+       static char *switch_off_status[] = {
+               "Swoff bit programming",
+               "Thermal protection activation",
+               "Vbat lower then BattOk falling threshold",
+               "Watchdog expired",
+               "Non presence of 32kHz clock",
+               "Battery level lower than power on reset threshold",
+               "Power on key 1 pressed longer than 10 seconds",
+               "DB8500 thermal shutdown"};
        struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
        const struct platform_device_id *platid = platform_get_device_id(pdev);
        enum ab8500_version version = AB8500_VERSION_UNDEFINED;
@@ -1233,14 +1274,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        if (plat)
                ab8500->irq_base = plat->irq_base;
-       else if (np)
-               ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base);
-
-       if (!ab8500->irq_base) {
-               dev_info(&pdev->dev, "couldn't find irq-base\n");
-               ret = -EINVAL;
-               goto out_free_ab8500;
-       }
 
        ab8500->dev = &pdev->dev;
 
@@ -1252,9 +1285,9 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        ab8500->irq = resource->start;
 
-       ab8500->read = ab8500_i2c_read;
-       ab8500->write = ab8500_i2c_write;
-       ab8500->write_masked = ab8500_i2c_write_masked;
+       ab8500->read = ab8500_prcmu_read;
+       ab8500->write = ab8500_prcmu_write;
+       ab8500->write_masked = ab8500_prcmu_write_masked;
 
        mutex_init(&ab8500->lock);
        mutex_init(&ab8500->irq_lock);
@@ -1264,9 +1297,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        if (platid)
                version = platid->driver_data;
-       else if (np)
-               version = (unsigned int)
-                       of_match_device(ab8500_match, &pdev->dev)->data;
 
        if (version != AB8500_VERSION_UNDEFINED)
                ab8500->version = version;
@@ -1323,7 +1353,20 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
                AB8500_SWITCH_OFF_STATUS, &value);
        if (ret < 0)
                return ret;
-       dev_info(ab8500->dev, "switch off status: %#x", value);
+       dev_info(ab8500->dev, "switch off cause(s) (%#x): ", value);
+
+       if (value) {
+               for (i = 0; i < ARRAY_SIZE(switch_off_status); i++) {
+                       if (value & 1)
+                               printk(KERN_CONT " \"%s\"",
+                                      switch_off_status[i]);
+                       value = value >> 1;
+
+               }
+               printk(KERN_CONT "\n");
+       } else {
+               printk(KERN_CONT " None\n");
+       }
 
        if (plat && plat->init)
                plat->init(ab8500);
@@ -1352,59 +1395,56 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
        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)
-                       goto out_freeoldmask;
+       ret = ab8500_irq_init(ab8500, np);
+       if (ret)
+               goto out_freeoldmask;
 
-               /*  Activate this feature only in ab9540 */
-               /*  till tests are done on ab8500 1p2 or later*/
-               if (is_ab9540(ab8500))
-                       ret = request_threaded_irq(ab8500->irq, NULL,
+       /*  Activate this feature only in ab9540 */
+       /*  till tests are done on ab8500 1p2 or later*/
+       if (is_ab9540(ab8500)) {
+               ret = request_threaded_irq(ab8500->irq, NULL,
                                        ab8500_hierarchical_irq,
                                        IRQF_ONESHOT | IRQF_NO_SUSPEND,
                                        "ab8500", ab8500);
-               else
-                       ret = request_threaded_irq(ab8500->irq, NULL,
+       }
+       else {
+               ret = request_threaded_irq(ab8500->irq, NULL,
                                        ab8500_irq,
                                        IRQF_ONESHOT | IRQF_NO_SUSPEND,
                                        "ab8500", ab8500);
                if (ret)
-                       goto out_removeirq;
+                       goto out_freeoldmask;
        }
 
-       if (!np) {
-               ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
-                               ARRAY_SIZE(abx500_common_devs), NULL,
-                               ab8500->irq_base);
+       ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
+                       ARRAY_SIZE(abx500_common_devs), NULL,
+                       ab8500->irq_base, ab8500->domain);
+       if (ret)
+               goto out_freeirq;
 
-               if (ret)
-                       goto out_freeirq;
-
-               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(ab8500_devs), NULL,
-                                       ab8500->irq_base);
-               if (ret)
-                       goto out_freeirq;
+       if (is_ab9540(ab8500))
+               ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
+                               ARRAY_SIZE(ab9540_devs), NULL,
+                               ab8500->irq_base, ab8500->domain);
+       else
+               ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
+                               ARRAY_SIZE(ab8500_devs), NULL,
+                               ab8500->irq_base, ab8500->domain);
+       if (ret)
+               goto out_freeirq;
 
-               if (is_ab9540(ab8500) || is_ab8505(ab8500))
-                       ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
-                                       ARRAY_SIZE(ab9540_ab8505_devs), NULL,
-                                       ab8500->irq_base);
-               if (ret)
-                       goto out_freeirq;
-       }
+       if (is_ab9540(ab8500) || is_ab8505(ab8500))
+               ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
+                               ARRAY_SIZE(ab9540_ab8505_devs), NULL,
+                               ab8500->irq_base, ab8500->domain);
+       if (ret)
+               goto out_freeirq;
 
        if (!no_bm) {
                /* Add battery management devices */
                ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
                                      ARRAY_SIZE(ab8500_bm_devs), NULL,
-                                     ab8500->irq_base);
+                                     ab8500->irq_base, ab8500->domain);
                if (ret)
                        dev_err(ab8500->dev, "error adding bm devices\n");
        }
@@ -1417,15 +1457,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
                                        &ab8500_attr_group);
        if (ret)
                dev_err(ab8500->dev, "error creating sysfs entries\n");
-       else
-               return ret;
+
+       return ret;
 
 out_freeirq:
-       if (ab8500->irq_base)
-               free_irq(ab8500->irq, ab8500);
-out_removeirq:
-       if (ab8500->irq_base)
-               ab8500_irq_remove(ab8500);
+       free_irq(ab8500->irq, ab8500);
 out_freeoldmask:
        kfree(ab8500->oldmask);
 out_freemask:
@@ -1444,11 +1480,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev)
                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);
-       }
+       free_irq(ab8500->irq, ab8500);
+
        kfree(ab8500->oldmask);
        kfree(ab8500->mask);
        kfree(ab8500);
@@ -1468,7 +1503,6 @@ static struct platform_driver ab8500_core_driver = {
        .driver = {
                .name = "ab8500-core",
                .owner = THIS_MODULE,
-               .of_match_table = ab8500_match,
        },
        .probe  = ab8500_probe,
        .remove = __devexit_p(ab8500_remove),
@@ -1484,7 +1518,7 @@ static void __exit ab8500_core_exit(void)
 {
        platform_driver_unregister(&ab8500_core_driver);
 }
-arch_initcall(ab8500_core_init);
+core_initcall(ab8500_core_init);
 module_exit(ab8500_core_exit);
 
 MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");