linux/drivers/mfd/lp8788.c
<<
>>
Prefs
   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
  41static 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
  65static 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
  74static 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
 126int 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}
 140EXPORT_SYMBOL_GPL(lp8788_read_byte);
 141
 142int 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}
 146EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes);
 147
 148int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data)
 149{
 150        return regmap_write(lp->regmap, reg, data);
 151}
 152EXPORT_SYMBOL_GPL(lp8788_write_byte);
 153
 154int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data)
 155{
 156        return regmap_update_bits(lp->regmap, reg, mask, data);
 157}
 158EXPORT_SYMBOL_GPL(lp8788_update_bits);
 159
 160static 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
 167static const struct regmap_config lp8788_regmap_config = {
 168        .reg_bits = 8,
 169        .val_bits = 8,
 170        .max_register = MAX_LP8788_REGISTERS,
 171};
 172
 173static 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
 206static int 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
 215static const struct i2c_device_id lp8788_ids[] = {
 216        {"lp8788", 0},
 217        { }
 218};
 219MODULE_DEVICE_TABLE(i2c, lp8788_ids);
 220
 221static struct i2c_driver lp8788_driver = {
 222        .driver = {
 223                .name = "lp8788",
 224                .owner = THIS_MODULE,
 225        },
 226        .probe = lp8788_probe,
 227        .remove = lp8788_remove,
 228        .id_table = lp8788_ids,
 229};
 230
 231static int __init lp8788_init(void)
 232{
 233        return i2c_add_driver(&lp8788_driver);
 234}
 235subsys_initcall(lp8788_init);
 236
 237static void __exit lp8788_exit(void)
 238{
 239        i2c_del_driver(&lp8788_driver);
 240}
 241module_exit(lp8788_exit);
 242
 243MODULE_DESCRIPTION("TI LP8788 MFD Driver");
 244MODULE_AUTHOR("Milo Kim");
 245MODULE_LICENSE("GPL");
 246