Commit eea6b7cc53aaecf868e1643058159807c744e04e

Authored by Milo Kim
Committed by Samuel Ortiz
1 parent 804971ec37

mfd: Add lp8788 mfd driver

TI LP8788 PMU provides regulators, battery charger, ADC,
RTC, backlight driver and current sinks.

This MFD patch supports the I2C communication using the regmap,
the interrupt handling using the linear IRQ domain and
configurable platform data structures for each driver module.

 (Driver Architecture)

                                         < mfd devices >
  LP8788 HW  ..........  mfd  .......... regulator drivers
                I2C                      power supply driver
                IRQs                     iio adc driver
                                         rtc driver
                                         backlight driver
                                         current sink drivers

  o regulators    : LDOs and BUCKs
  o power supply  : Battery charger
  o iio adc       : Battery voltage/temperature
  o rtc           : RTC and alarm
  o backlight
  o current sink  : LED and vibrator

All MFD device modules are registered by LP8788 MFD core driver.
For sharing information such like the virtual IRQ number,
MFD core driver uses the resource structure.
Then each module can retrieve the specific IRQ number and detect it
in the IRQ thread.

Configurable platform data is handled in each driver module.

Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

Showing 6 changed files with 871 additions and 0 deletions Side-by-side Diff

... ... @@ -440,6 +440,16 @@
440 440 individual components like LCD backlight, LEDs, GPIOs and Kepad
441 441 under the corresponding menus.
442 442  
  443 +config MFD_LP8788
  444 + bool "Texas Instruments LP8788 Power Management Unit Driver"
  445 + depends on I2C=y
  446 + select MFD_CORE
  447 + select REGMAP_I2C
  448 + select IRQ_DOMAIN
  449 + help
  450 + TI LP8788 PMU supports regulators, battery charger, RTC,
  451 + ADC, backlight driver and current sinks.
  452 +
443 453 config MFD_MAX77686
444 454 bool "Maxim Semiconductor MAX77686 PMIC Support"
445 455 depends on I2C=y && GENERIC_HARDIRQS
drivers/mfd/Makefile
... ... @@ -89,6 +89,8 @@
89 89 obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
90 90 obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
91 91  
  92 +obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
  93 +
92 94 obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
93 95 obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
94 96 obj-$(CONFIG_MFD_MAX8907) += max8907.o
drivers/mfd/lp8788-irq.c
  1 +/*
  2 + * TI LP8788 MFD - interrupt handler
  3 + *
  4 + * Copyright 2012 Texas Instruments
  5 + *
  6 + * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + *
  12 + */
  13 +
  14 +#include <linux/delay.h>
  15 +#include <linux/err.h>
  16 +#include <linux/interrupt.h>
  17 +#include <linux/irq.h>
  18 +#include <linux/irqdomain.h>
  19 +#include <linux/device.h>
  20 +#include <linux/mfd/lp8788.h>
  21 +#include <linux/module.h>
  22 +#include <linux/slab.h>
  23 +
  24 +/* register address */
  25 +#define LP8788_INT_1 0x00
  26 +#define LP8788_INTEN_1 0x03
  27 +
  28 +#define BASE_INTEN_ADDR LP8788_INTEN_1
  29 +#define SIZE_REG 8
  30 +#define NUM_REGS 3
  31 +
  32 +/*
  33 + * struct lp8788_irq_data
  34 + * @lp : used for accessing to lp8788 registers
  35 + * @irq_lock : mutex for enabling/disabling the interrupt
  36 + * @domain : IRQ domain for handling nested interrupt
  37 + * @enabled : status of enabled interrupt
  38 + */
  39 +struct lp8788_irq_data {
  40 + struct lp8788 *lp;
  41 + struct mutex irq_lock;
  42 + struct irq_domain *domain;
  43 + int enabled[LP8788_INT_MAX];
  44 +};
  45 +
  46 +static inline u8 _irq_to_addr(enum lp8788_int_id id)
  47 +{
  48 + return id / SIZE_REG;
  49 +}
  50 +
  51 +static inline u8 _irq_to_enable_addr(enum lp8788_int_id id)
  52 +{
  53 + return _irq_to_addr(id) + BASE_INTEN_ADDR;
  54 +}
  55 +
  56 +static inline u8 _irq_to_mask(enum lp8788_int_id id)
  57 +{
  58 + return 1 << (id % SIZE_REG);
  59 +}
  60 +
  61 +static inline u8 _irq_to_val(enum lp8788_int_id id, int enable)
  62 +{
  63 + return enable << (id % SIZE_REG);
  64 +}
  65 +
  66 +static void lp8788_irq_enable(struct irq_data *data)
  67 +{
  68 + struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  69 + irqd->enabled[data->hwirq] = 1;
  70 +}
  71 +
  72 +static void lp8788_irq_disable(struct irq_data *data)
  73 +{
  74 + struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  75 + irqd->enabled[data->hwirq] = 0;
  76 +}
  77 +
  78 +static void lp8788_irq_bus_lock(struct irq_data *data)
  79 +{
  80 + struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  81 +
  82 + mutex_lock(&irqd->irq_lock);
  83 +}
  84 +
  85 +static void lp8788_irq_bus_sync_unlock(struct irq_data *data)
  86 +{
  87 + struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
  88 + enum lp8788_int_id irq = data->hwirq;
  89 + u8 addr, mask, val;
  90 +
  91 + addr = _irq_to_enable_addr(irq);
  92 + mask = _irq_to_mask(irq);
  93 + val = _irq_to_val(irq, irqd->enabled[irq]);
  94 +
  95 + lp8788_update_bits(irqd->lp, addr, mask, val);
  96 +
  97 + mutex_unlock(&irqd->irq_lock);
  98 +}
  99 +
  100 +static struct irq_chip lp8788_irq_chip = {
  101 + .name = "lp8788",
  102 + .irq_enable = lp8788_irq_enable,
  103 + .irq_disable = lp8788_irq_disable,
  104 + .irq_bus_lock = lp8788_irq_bus_lock,
  105 + .irq_bus_sync_unlock = lp8788_irq_bus_sync_unlock,
  106 +};
  107 +
  108 +static irqreturn_t lp8788_irq_handler(int irq, void *ptr)
  109 +{
  110 + struct lp8788_irq_data *irqd = ptr;
  111 + struct lp8788 *lp = irqd->lp;
  112 + u8 status[NUM_REGS], addr, mask;
  113 + bool handled;
  114 + int i;
  115 +
  116 + if (lp8788_read_multi_bytes(lp, LP8788_INT_1, status, NUM_REGS))
  117 + return IRQ_NONE;
  118 +
  119 + for (i = 0 ; i < LP8788_INT_MAX ; i++) {
  120 + addr = _irq_to_addr(i);
  121 + mask = _irq_to_mask(i);
  122 +
  123 + /* reporting only if the irq is enabled */
  124 + if (status[addr] & mask) {
  125 + handle_nested_irq(irq_find_mapping(irqd->domain, i));
  126 + handled = true;
  127 + }
  128 + }
  129 +
  130 + return handled ? IRQ_HANDLED : IRQ_NONE;
  131 +}
  132 +
  133 +static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
  134 + irq_hw_number_t hwirq)
  135 +{
  136 + struct lp8788_irq_data *irqd = d->host_data;
  137 + struct irq_chip *chip = &lp8788_irq_chip;
  138 +
  139 + irq_set_chip_data(virq, irqd);
  140 + irq_set_chip_and_handler(virq, chip, handle_edge_irq);
  141 + irq_set_nested_thread(virq, 1);
  142 +
  143 +#ifdef CONFIG_ARM
  144 + set_irq_flags(virq, IRQF_VALID);
  145 +#else
  146 + irq_set_noprobe(virq);
  147 +#endif
  148 +
  149 + return 0;
  150 +}
  151 +
  152 +static struct irq_domain_ops lp8788_domain_ops = {
  153 + .map = lp8788_irq_map,
  154 +};
  155 +
  156 +int lp8788_irq_init(struct lp8788 *lp, int irq)
  157 +{
  158 + struct lp8788_irq_data *irqd;
  159 + int ret;
  160 +
  161 + if (irq <= 0) {
  162 + dev_warn(lp->dev, "invalid irq number: %d\n", irq);
  163 + return 0;
  164 + }
  165 +
  166 + irqd = devm_kzalloc(lp->dev, sizeof(*irqd), GFP_KERNEL);
  167 + if (!irqd)
  168 + return -ENOMEM;
  169 +
  170 + irqd->lp = lp;
  171 + irqd->domain = irq_domain_add_linear(lp->dev->of_node, LP8788_INT_MAX,
  172 + &lp8788_domain_ops, irqd);
  173 + if (!irqd->domain) {
  174 + dev_err(lp->dev, "failed to add irq domain err\n");
  175 + return -EINVAL;
  176 + }
  177 +
  178 + lp->irqdm = irqd->domain;
  179 + mutex_init(&irqd->irq_lock);
  180 +
  181 + ret = request_threaded_irq(irq, NULL, lp8788_irq_handler,
  182 + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
  183 + "lp8788-irq", irqd);
  184 + if (ret) {
  185 + dev_err(lp->dev, "failed to create a thread for IRQ_N\n");
  186 + return ret;
  187 + }
  188 +
  189 + lp->irq = irq;
  190 +
  191 + return 0;
  192 +}
  193 +
  194 +void lp8788_irq_exit(struct lp8788 *lp)
  195 +{
  196 + if (lp->irq)
  197 + free_irq(lp->irq, lp->irqdm);
  198 +}
drivers/mfd/lp8788.c
  1 +/*
  2 + * TI LP8788 MFD - core interface
  3 + *
  4 + * Copyright 2012 Texas Instruments
  5 + *
  6 + * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + *
  12 + */
  13 +
  14 +#include <linux/err.h>
  15 +#include <linux/i2c.h>
  16 +#include <linux/mfd/core.h>
  17 +#include <linux/mfd/lp8788.h>
  18 +#include <linux/module.h>
  19 +#include <linux/slab.h>
  20 +
  21 +#define MAX_LP8788_REGISTERS 0xA2
  22 +
  23 +#define MFD_DEV_SIMPLE(_name) \
  24 +{ \
  25 + .name = LP8788_DEV_##_name, \
  26 +}
  27 +
  28 +#define MFD_DEV_WITH_ID(_name, _id) \
  29 +{ \
  30 + .name = LP8788_DEV_##_name, \
  31 + .id = _id, \
  32 +}
  33 +
  34 +#define MFD_DEV_WITH_RESOURCE(_name, _resource, num_resource) \
  35 +{ \
  36 + .name = LP8788_DEV_##_name, \
  37 + .resources = _resource, \
  38 + .num_resources = num_resource, \
  39 +}
  40 +
  41 +static struct resource chg_irqs[] = {
  42 + /* Charger Interrupts */
  43 + {
  44 + .start = LP8788_INT_CHG_INPUT_STATE,
  45 + .end = LP8788_INT_PRECHG_TIMEOUT,
  46 + .name = LP8788_CHG_IRQ,
  47 + .flags = IORESOURCE_IRQ,
  48 + },
  49 + /* Power Routing Switch Interrupts */
  50 + {
  51 + .start = LP8788_INT_ENTER_SYS_SUPPORT,
  52 + .end = LP8788_INT_EXIT_SYS_SUPPORT,
  53 + .name = LP8788_PRSW_IRQ,
  54 + .flags = IORESOURCE_IRQ,
  55 + },
  56 + /* Battery Interrupts */
  57 + {
  58 + .start = LP8788_INT_BATT_LOW,
  59 + .end = LP8788_INT_NO_BATT,
  60 + .name = LP8788_BATT_IRQ,
  61 + .flags = IORESOURCE_IRQ,
  62 + },
  63 +};
  64 +
  65 +static struct resource rtc_irqs[] = {
  66 + {
  67 + .start = LP8788_INT_RTC_ALARM1,
  68 + .end = LP8788_INT_RTC_ALARM2,
  69 + .name = LP8788_ALM_IRQ,
  70 + .flags = IORESOURCE_IRQ,
  71 + },
  72 +};
  73 +
  74 +static struct mfd_cell lp8788_devs[] = {
  75 + /* 4 bucks */
  76 + MFD_DEV_WITH_ID(BUCK, 1),
  77 + MFD_DEV_WITH_ID(BUCK, 2),
  78 + MFD_DEV_WITH_ID(BUCK, 3),
  79 + MFD_DEV_WITH_ID(BUCK, 4),
  80 +
  81 + /* 12 digital ldos */
  82 + MFD_DEV_WITH_ID(DLDO, 1),
  83 + MFD_DEV_WITH_ID(DLDO, 2),
  84 + MFD_DEV_WITH_ID(DLDO, 3),
  85 + MFD_DEV_WITH_ID(DLDO, 4),
  86 + MFD_DEV_WITH_ID(DLDO, 5),
  87 + MFD_DEV_WITH_ID(DLDO, 6),
  88 + MFD_DEV_WITH_ID(DLDO, 7),
  89 + MFD_DEV_WITH_ID(DLDO, 8),
  90 + MFD_DEV_WITH_ID(DLDO, 9),
  91 + MFD_DEV_WITH_ID(DLDO, 10),
  92 + MFD_DEV_WITH_ID(DLDO, 11),
  93 + MFD_DEV_WITH_ID(DLDO, 12),
  94 +
  95 + /* 10 analog ldos */
  96 + MFD_DEV_WITH_ID(ALDO, 1),
  97 + MFD_DEV_WITH_ID(ALDO, 2),
  98 + MFD_DEV_WITH_ID(ALDO, 3),
  99 + MFD_DEV_WITH_ID(ALDO, 4),
  100 + MFD_DEV_WITH_ID(ALDO, 5),
  101 + MFD_DEV_WITH_ID(ALDO, 6),
  102 + MFD_DEV_WITH_ID(ALDO, 7),
  103 + MFD_DEV_WITH_ID(ALDO, 8),
  104 + MFD_DEV_WITH_ID(ALDO, 9),
  105 + MFD_DEV_WITH_ID(ALDO, 10),
  106 +
  107 + /* ADC */
  108 + MFD_DEV_SIMPLE(ADC),
  109 +
  110 + /* battery charger */
  111 + MFD_DEV_WITH_RESOURCE(CHARGER, chg_irqs, ARRAY_SIZE(chg_irqs)),
  112 +
  113 + /* rtc */
  114 + MFD_DEV_WITH_RESOURCE(RTC, rtc_irqs, ARRAY_SIZE(rtc_irqs)),
  115 +
  116 + /* backlight */
  117 + MFD_DEV_SIMPLE(BACKLIGHT),
  118 +
  119 + /* current sink for vibrator */
  120 + MFD_DEV_SIMPLE(VIBRATOR),
  121 +
  122 + /* current sink for keypad LED */
  123 + MFD_DEV_SIMPLE(KEYLED),
  124 +};
  125 +
  126 +int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data)
  127 +{
  128 + int ret;
  129 + unsigned int val;
  130 +
  131 + ret = regmap_read(lp->regmap, reg, &val);
  132 + if (ret < 0) {
  133 + dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
  134 + return ret;
  135 + }
  136 +
  137 + *data = (u8)val;
  138 + return 0;
  139 +}
  140 +EXPORT_SYMBOL_GPL(lp8788_read_byte);
  141 +
  142 +int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count)
  143 +{
  144 + return regmap_bulk_read(lp->regmap, reg, data, count);
  145 +}
  146 +EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes);
  147 +
  148 +int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data)
  149 +{
  150 + return regmap_write(lp->regmap, reg, data);
  151 +}
  152 +EXPORT_SYMBOL_GPL(lp8788_write_byte);
  153 +
  154 +int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data)
  155 +{
  156 + return regmap_update_bits(lp->regmap, reg, mask, data);
  157 +}
  158 +EXPORT_SYMBOL_GPL(lp8788_update_bits);
  159 +
  160 +static int lp8788_platform_init(struct lp8788 *lp)
  161 +{
  162 + struct lp8788_platform_data *pdata = lp->pdata;
  163 +
  164 + return (pdata && pdata->init_func) ? pdata->init_func(lp) : 0;
  165 +}
  166 +
  167 +static const struct regmap_config lp8788_regmap_config = {
  168 + .reg_bits = 8,
  169 + .val_bits = 8,
  170 + .max_register = MAX_LP8788_REGISTERS,
  171 +};
  172 +
  173 +static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
  174 +{
  175 + struct lp8788 *lp;
  176 + struct lp8788_platform_data *pdata = cl->dev.platform_data;
  177 + int ret;
  178 +
  179 + lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL);
  180 + if (!lp)
  181 + return -ENOMEM;
  182 +
  183 + lp->regmap = devm_regmap_init_i2c(cl, &lp8788_regmap_config);
  184 + if (IS_ERR(lp->regmap)) {
  185 + ret = PTR_ERR(lp->regmap);
  186 + dev_err(&cl->dev, "regmap init i2c err: %d\n", ret);
  187 + return ret;
  188 + }
  189 +
  190 + lp->pdata = pdata;
  191 + lp->dev = &cl->dev;
  192 + i2c_set_clientdata(cl, lp);
  193 +
  194 + ret = lp8788_platform_init(lp);
  195 + if (ret)
  196 + return ret;
  197 +
  198 + ret = lp8788_irq_init(lp, cl->irq);
  199 + if (ret)
  200 + return ret;
  201 +
  202 + return mfd_add_devices(lp->dev, -1, lp8788_devs,
  203 + ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
  204 +}
  205 +
  206 +static int __devexit lp8788_remove(struct i2c_client *cl)
  207 +{
  208 + struct lp8788 *lp = i2c_get_clientdata(cl);
  209 +
  210 + mfd_remove_devices(lp->dev);
  211 + lp8788_irq_exit(lp);
  212 + return 0;
  213 +}
  214 +
  215 +static const struct i2c_device_id lp8788_ids[] = {
  216 + {"lp8788", 0},
  217 + { }
  218 +};
  219 +MODULE_DEVICE_TABLE(i2c, lp8788_ids);
  220 +
  221 +static struct i2c_driver lp8788_driver = {
  222 + .driver = {
  223 + .name = "lp8788",
  224 + .owner = THIS_MODULE,
  225 + },
  226 + .probe = lp8788_probe,
  227 + .remove = __devexit_p(lp8788_remove),
  228 + .id_table = lp8788_ids,
  229 +};
  230 +
  231 +static int __init lp8788_init(void)
  232 +{
  233 + return i2c_add_driver(&lp8788_driver);
  234 +}
  235 +subsys_initcall(lp8788_init);
  236 +
  237 +static void __exit lp8788_exit(void)
  238 +{
  239 + i2c_del_driver(&lp8788_driver);
  240 +}
  241 +module_exit(lp8788_exit);
  242 +
  243 +MODULE_DESCRIPTION("TI LP8788 MFD Driver");
  244 +MODULE_AUTHOR("Milo Kim");
  245 +MODULE_LICENSE("GPL");
include/linux/mfd/lp8788-isink.h
  1 +/*
  2 + * TI LP8788 MFD - common definitions for current sinks
  3 + *
  4 + * Copyright 2012 Texas Instruments
  5 + *
  6 + * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + *
  12 + */
  13 +
  14 +#ifndef __ISINK_LP8788_H__
  15 +#define __ISINK_LP8788_H__
  16 +
  17 +/* register address */
  18 +#define LP8788_ISINK_CTRL 0x99
  19 +#define LP8788_ISINK12_IOUT 0x9A
  20 +#define LP8788_ISINK3_IOUT 0x9B
  21 +#define LP8788_ISINK1_PWM 0x9C
  22 +#define LP8788_ISINK2_PWM 0x9D
  23 +#define LP8788_ISINK3_PWM 0x9E
  24 +
  25 +/* mask bits */
  26 +#define LP8788_ISINK1_IOUT_M 0x0F /* Addr 9Ah */
  27 +#define LP8788_ISINK2_IOUT_M 0xF0
  28 +#define LP8788_ISINK3_IOUT_M 0x0F /* Addr 9Bh */
  29 +
  30 +/* 6 bits used for PWM code : Addr 9C ~ 9Eh */
  31 +#define LP8788_ISINK_MAX_PWM 63
  32 +#define LP8788_ISINK_SCALE_OFFSET 3
  33 +
  34 +static const u8 lp8788_iout_addr[] = {
  35 + LP8788_ISINK12_IOUT,
  36 + LP8788_ISINK12_IOUT,
  37 + LP8788_ISINK3_IOUT,
  38 +};
  39 +
  40 +static const u8 lp8788_iout_mask[] = {
  41 + LP8788_ISINK1_IOUT_M,
  42 + LP8788_ISINK2_IOUT_M,
  43 + LP8788_ISINK3_IOUT_M,
  44 +};
  45 +
  46 +static const u8 lp8788_pwm_addr[] = {
  47 + LP8788_ISINK1_PWM,
  48 + LP8788_ISINK2_PWM,
  49 + LP8788_ISINK3_PWM,
  50 +};
  51 +
  52 +#endif
include/linux/mfd/lp8788.h
  1 +/*
  2 + * TI LP8788 MFD Device
  3 + *
  4 + * Copyright 2012 Texas Instruments
  5 + *
  6 + * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + *
  12 + */
  13 +
  14 +#ifndef __MFD_LP8788_H__
  15 +#define __MFD_LP8788_H__
  16 +
  17 +#include <linux/gpio.h>
  18 +#include <linux/irqdomain.h>
  19 +#include <linux/regmap.h>
  20 +
  21 +#define LP8788_DEV_BUCK "lp8788-buck"
  22 +#define LP8788_DEV_DLDO "lp8788-dldo"
  23 +#define LP8788_DEV_ALDO "lp8788-aldo"
  24 +#define LP8788_DEV_CHARGER "lp8788-charger"
  25 +#define LP8788_DEV_RTC "lp8788-rtc"
  26 +#define LP8788_DEV_BACKLIGHT "lp8788-backlight"
  27 +#define LP8788_DEV_VIBRATOR "lp8788-vibrator"
  28 +#define LP8788_DEV_KEYLED "lp8788-keyled"
  29 +#define LP8788_DEV_ADC "lp8788-adc"
  30 +
  31 +#define LP8788_NUM_BUCKS 4
  32 +#define LP8788_NUM_DLDOS 12
  33 +#define LP8788_NUM_ALDOS 10
  34 +#define LP8788_NUM_BUCK2_DVS 2
  35 +
  36 +#define LP8788_CHG_IRQ "CHG_IRQ"
  37 +#define LP8788_PRSW_IRQ "PRSW_IRQ"
  38 +#define LP8788_BATT_IRQ "BATT_IRQ"
  39 +#define LP8788_ALM_IRQ "ALARM_IRQ"
  40 +
  41 +enum lp8788_int_id {
  42 + /* interrup register 1 : Addr 00h */
  43 + LP8788_INT_TSDL,
  44 + LP8788_INT_TSDH,
  45 + LP8788_INT_UVLO,
  46 + LP8788_INT_FLAGMON,
  47 + LP8788_INT_PWRON_TIME,
  48 + LP8788_INT_PWRON,
  49 + LP8788_INT_COMP1,
  50 + LP8788_INT_COMP2,
  51 +
  52 + /* interrupt register 2 : Addr 01h */
  53 + LP8788_INT_CHG_INPUT_STATE,
  54 + LP8788_INT_CHG_STATE,
  55 + LP8788_INT_EOC,
  56 + LP8788_INT_CHG_RESTART,
  57 + LP8788_INT_RESTART_TIMEOUT,
  58 + LP8788_INT_FULLCHG_TIMEOUT,
  59 + LP8788_INT_PRECHG_TIMEOUT,
  60 +
  61 + /* interrupt register 3 : Addr 02h */
  62 + LP8788_INT_RTC_ALARM1 = 17,
  63 + LP8788_INT_RTC_ALARM2,
  64 + LP8788_INT_ENTER_SYS_SUPPORT,
  65 + LP8788_INT_EXIT_SYS_SUPPORT,
  66 + LP8788_INT_BATT_LOW,
  67 + LP8788_INT_NO_BATT,
  68 +
  69 + LP8788_INT_MAX = 24,
  70 +};
  71 +
  72 +enum lp8788_dvs_sel {
  73 + DVS_SEL_V0,
  74 + DVS_SEL_V1,
  75 + DVS_SEL_V2,
  76 + DVS_SEL_V3,
  77 +};
  78 +
  79 +enum lp8788_ext_ldo_en_id {
  80 + EN_ALDO1,
  81 + EN_ALDO234,
  82 + EN_ALDO5,
  83 + EN_ALDO7,
  84 + EN_DLDO7,
  85 + EN_DLDO911,
  86 + EN_LDOS_MAX,
  87 +};
  88 +
  89 +enum lp8788_charger_event {
  90 + NO_CHARGER,
  91 + CHARGER_DETECTED,
  92 +};
  93 +
  94 +enum lp8788_bl_ctrl_mode {
  95 + LP8788_BL_REGISTER_ONLY,
  96 + LP8788_BL_COMB_PWM_BASED, /* PWM + I2C, changed by PWM input */
  97 + LP8788_BL_COMB_REGISTER_BASED, /* PWM + I2C, changed by I2C */
  98 +};
  99 +
  100 +enum lp8788_bl_dim_mode {
  101 + LP8788_DIM_EXPONENTIAL,
  102 + LP8788_DIM_LINEAR,
  103 +};
  104 +
  105 +enum lp8788_bl_full_scale_current {
  106 + LP8788_FULLSCALE_5000uA,
  107 + LP8788_FULLSCALE_8500uA,
  108 + LP8788_FULLSCALE_1200uA,
  109 + LP8788_FULLSCALE_1550uA,
  110 + LP8788_FULLSCALE_1900uA,
  111 + LP8788_FULLSCALE_2250uA,
  112 + LP8788_FULLSCALE_2600uA,
  113 + LP8788_FULLSCALE_2950uA,
  114 +};
  115 +
  116 +enum lp8788_bl_ramp_step {
  117 + LP8788_RAMP_8us,
  118 + LP8788_RAMP_1024us,
  119 + LP8788_RAMP_2048us,
  120 + LP8788_RAMP_4096us,
  121 + LP8788_RAMP_8192us,
  122 + LP8788_RAMP_16384us,
  123 + LP8788_RAMP_32768us,
  124 + LP8788_RAMP_65538us,
  125 +};
  126 +
  127 +enum lp8788_bl_pwm_polarity {
  128 + LP8788_PWM_ACTIVE_HIGH,
  129 + LP8788_PWM_ACTIVE_LOW,
  130 +};
  131 +
  132 +enum lp8788_isink_scale {
  133 + LP8788_ISINK_SCALE_100mA,
  134 + LP8788_ISINK_SCALE_120mA,
  135 +};
  136 +
  137 +enum lp8788_isink_number {
  138 + LP8788_ISINK_1,
  139 + LP8788_ISINK_2,
  140 + LP8788_ISINK_3,
  141 +};
  142 +
  143 +enum lp8788_alarm_sel {
  144 + LP8788_ALARM_1,
  145 + LP8788_ALARM_2,
  146 + LP8788_ALARM_MAX,
  147 +};
  148 +
  149 +enum lp8788_adc_id {
  150 + LPADC_VBATT_5P5,
  151 + LPADC_VIN_CHG,
  152 + LPADC_IBATT,
  153 + LPADC_IC_TEMP,
  154 + LPADC_VBATT_6P0,
  155 + LPADC_VBATT_5P0,
  156 + LPADC_ADC1,
  157 + LPADC_ADC2,
  158 + LPADC_VDD,
  159 + LPADC_VCOIN,
  160 + LPADC_VDD_LDO,
  161 + LPADC_ADC3,
  162 + LPADC_ADC4,
  163 + LPADC_MAX,
  164 +};
  165 +
  166 +struct lp8788;
  167 +
  168 +/*
  169 + * lp8788_buck1_dvs
  170 + * @gpio : gpio pin number for dvs control
  171 + * @vsel : dvs selector for buck v1 register
  172 + */
  173 +struct lp8788_buck1_dvs {
  174 + int gpio;
  175 + enum lp8788_dvs_sel vsel;
  176 +};
  177 +
  178 +/*
  179 + * lp8788_buck2_dvs
  180 + * @gpio : two gpio pin numbers are used for dvs
  181 + * @vsel : dvs selector for buck v2 register
  182 + */
  183 +struct lp8788_buck2_dvs {
  184 + int gpio[LP8788_NUM_BUCK2_DVS];
  185 + enum lp8788_dvs_sel vsel;
  186 +};
  187 +
  188 +/*
  189 + * struct lp8788_ldo_enable_pin
  190 + *
  191 + * Basically, all LDOs are enabled through the I2C commands.
  192 + * But ALDO 1 ~ 5, 7, DLDO 7, 9, 11 can be enabled by external gpio pins.
  193 + *
  194 + * @gpio : gpio number which is used for enabling ldos
  195 + * @init_state : initial gpio state (ex. GPIOF_OUT_INIT_LOW)
  196 + */
  197 +struct lp8788_ldo_enable_pin {
  198 + int gpio;
  199 + int init_state;
  200 +};
  201 +
  202 +/*
  203 + * struct lp8788_chg_param
  204 + * @addr : charging control register address (range : 0x11 ~ 0x1C)
  205 + * @val : charging parameter value
  206 + */
  207 +struct lp8788_chg_param {
  208 + u8 addr;
  209 + u8 val;
  210 +};
  211 +
  212 +/*
  213 + * struct lp8788_charger_platform_data
  214 + * @vbatt_adc : adc selection id for battery voltage
  215 + * @batt_temp_adc : adc selection id for battery temperature
  216 + * @max_vbatt_mv : used for calculating battery capacity
  217 + * @chg_params : initial charging parameters
  218 + * @num_chg_params : numbers of charging parameters
  219 + * @charger_event : the charger event can be reported to the platform side
  220 + */
  221 +struct lp8788_charger_platform_data {
  222 + enum lp8788_adc_id vbatt_adc;
  223 + enum lp8788_adc_id batt_temp_adc;
  224 + unsigned int max_vbatt_mv;
  225 + struct lp8788_chg_param *chg_params;
  226 + int num_chg_params;
  227 + void (*charger_event) (struct lp8788 *lp,
  228 + enum lp8788_charger_event event);
  229 +};
  230 +
  231 +/*
  232 + * struct lp8788_bl_pwm_data
  233 + * @pwm_set_intensity : set duty of pwm
  234 + * @pwm_get_intensity : get current duty of pwm
  235 + */
  236 +struct lp8788_bl_pwm_data {
  237 + void (*pwm_set_intensity) (int brightness, int max_brightness);
  238 + int (*pwm_get_intensity) (int max_brightness);
  239 +};
  240 +
  241 +/*
  242 + * struct lp8788_backlight_platform_data
  243 + * @name : backlight driver name. (default: "lcd-backlight")
  244 + * @initial_brightness : initial value of backlight brightness
  245 + * @bl_mode : brightness control by pwm or lp8788 register
  246 + * @dim_mode : dimming mode selection
  247 + * @full_scale : full scale current setting
  248 + * @rise_time : brightness ramp up step time
  249 + * @fall_time : brightness ramp down step time
  250 + * @pwm_pol : pwm polarity setting when bl_mode is pwm based
  251 + * @pwm_data : platform specific pwm generation functions
  252 + * only valid when bl_mode is pwm based
  253 + */
  254 +struct lp8788_backlight_platform_data {
  255 + char *name;
  256 + int initial_brightness;
  257 + enum lp8788_bl_ctrl_mode bl_mode;
  258 + enum lp8788_bl_dim_mode dim_mode;
  259 + enum lp8788_bl_full_scale_current full_scale;
  260 + enum lp8788_bl_ramp_step rise_time;
  261 + enum lp8788_bl_ramp_step fall_time;
  262 + enum lp8788_bl_pwm_polarity pwm_pol;
  263 + struct lp8788_bl_pwm_data pwm_data;
  264 +};
  265 +
  266 +/*
  267 + * struct lp8788_led_platform_data
  268 + * @name : led driver name. (default: "keyboard-backlight")
  269 + * @scale : current scale
  270 + * @num : current sink number
  271 + * @iout_code : current output value (Addr 9Ah ~ 9Bh)
  272 + */
  273 +struct lp8788_led_platform_data {
  274 + char *name;
  275 + enum lp8788_isink_scale scale;
  276 + enum lp8788_isink_number num;
  277 + int iout_code;
  278 +};
  279 +
  280 +/*
  281 + * struct lp8788_vib_platform_data
  282 + * @name : vibrator driver name
  283 + * @scale : current scale
  284 + * @num : current sink number
  285 + * @iout_code : current output value (Addr 9Ah ~ 9Bh)
  286 + * @pwm_code : PWM code value (Addr 9Ch ~ 9Eh)
  287 + */
  288 +struct lp8788_vib_platform_data {
  289 + char *name;
  290 + enum lp8788_isink_scale scale;
  291 + enum lp8788_isink_number num;
  292 + int iout_code;
  293 + int pwm_code;
  294 +};
  295 +
  296 +/*
  297 + * struct lp8788_platform_data
  298 + * @init_func : used for initializing registers
  299 + * before mfd driver is registered
  300 + * @buck_data : regulator initial data for buck
  301 + * @dldo_data : regulator initial data for digital ldo
  302 + * @aldo_data : regulator initial data for analog ldo
  303 + * @buck1_dvs : gpio configurations for buck1 dvs
  304 + * @buck2_dvs : gpio configurations for buck2 dvs
  305 + * @ldo_pin : gpio configurations for enabling LDOs
  306 + * @chg_pdata : platform data for charger driver
  307 + * @alarm_sel : rtc alarm selection (1 or 2)
  308 + * @bl_pdata : configurable data for backlight driver
  309 + * @led_pdata : configurable data for led driver
  310 + * @vib_pdata : configurable data for vibrator driver
  311 + * @adc_pdata : iio map data for adc driver
  312 + */
  313 +struct lp8788_platform_data {
  314 + /* general system information */
  315 + int (*init_func) (struct lp8788 *lp);
  316 +
  317 + /* regulators */
  318 + struct regulator_init_data *buck_data[LP8788_NUM_BUCKS];
  319 + struct regulator_init_data *dldo_data[LP8788_NUM_DLDOS];
  320 + struct regulator_init_data *aldo_data[LP8788_NUM_ALDOS];
  321 + struct lp8788_buck1_dvs *buck1_dvs;
  322 + struct lp8788_buck2_dvs *buck2_dvs;
  323 + struct lp8788_ldo_enable_pin *ldo_pin[EN_LDOS_MAX];
  324 +
  325 + /* charger */
  326 + struct lp8788_charger_platform_data *chg_pdata;
  327 +
  328 + /* rtc alarm */
  329 + enum lp8788_alarm_sel alarm_sel;
  330 +
  331 + /* backlight */
  332 + struct lp8788_backlight_platform_data *bl_pdata;
  333 +
  334 + /* current sinks */
  335 + struct lp8788_led_platform_data *led_pdata;
  336 + struct lp8788_vib_platform_data *vib_pdata;
  337 +
  338 + /* adc iio map data */
  339 + struct iio_map *adc_pdata;
  340 +};
  341 +
  342 +/*
  343 + * struct lp8788
  344 + * @dev : parent device pointer
  345 + * @regmap : used for i2c communcation on accessing registers
  346 + * @irqdm : interrupt domain for handling nested interrupt
  347 + * @irq : pin number of IRQ_N
  348 + * @pdata : lp8788 platform specific data
  349 + */
  350 +struct lp8788 {
  351 + struct device *dev;
  352 + struct regmap *regmap;
  353 + struct irq_domain *irqdm;
  354 + int irq;
  355 + struct lp8788_platform_data *pdata;
  356 +};
  357 +
  358 +int lp8788_irq_init(struct lp8788 *lp, int chip_irq);
  359 +void lp8788_irq_exit(struct lp8788 *lp);
  360 +int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data);
  361 +int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count);
  362 +int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data);
  363 +int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data);
  364 +#endif