linux/drivers/mfd/as3711.c
<<
>>
Prefs
   1/*
   2 * AS3711 PMIC MFC driver
   3 *
   4 * Copyright (C) 2012 Renesas Electronics Corporation
   5 * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the version 2 of the GNU General Public License as
   9 * published by the Free Software Foundation
  10 */
  11
  12#include <linux/device.h>
  13#include <linux/err.h>
  14#include <linux/i2c.h>
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/mfd/as3711.h>
  18#include <linux/mfd/core.h>
  19#include <linux/module.h>
  20#include <linux/regmap.h>
  21#include <linux/slab.h>
  22
  23enum {
  24        AS3711_REGULATOR,
  25        AS3711_BACKLIGHT,
  26};
  27
  28/*
  29 * Ok to have it static: it is only used during probing and multiple I2C devices
  30 * cannot be probed simultaneously. Just make sure to avoid stale data.
  31 */
  32static struct mfd_cell as3711_subdevs[] = {
  33        [AS3711_REGULATOR] = {.name = "as3711-regulator",},
  34        [AS3711_BACKLIGHT] = {.name = "as3711-backlight",},
  35};
  36
  37static bool as3711_volatile_reg(struct device *dev, unsigned int reg)
  38{
  39        switch (reg) {
  40        case AS3711_GPIO_SIGNAL_IN:
  41        case AS3711_INTERRUPT_STATUS_1:
  42        case AS3711_INTERRUPT_STATUS_2:
  43        case AS3711_INTERRUPT_STATUS_3:
  44        case AS3711_CHARGER_STATUS_1:
  45        case AS3711_CHARGER_STATUS_2:
  46        case AS3711_REG_STATUS:
  47                return true;
  48        }
  49        return false;
  50}
  51
  52static bool as3711_precious_reg(struct device *dev, unsigned int reg)
  53{
  54        switch (reg) {
  55        case AS3711_INTERRUPT_STATUS_1:
  56        case AS3711_INTERRUPT_STATUS_2:
  57        case AS3711_INTERRUPT_STATUS_3:
  58                return true;
  59        }
  60        return false;
  61}
  62
  63static bool as3711_readable_reg(struct device *dev, unsigned int reg)
  64{
  65        switch (reg) {
  66        case AS3711_SD_1_VOLTAGE:
  67        case AS3711_SD_2_VOLTAGE:
  68        case AS3711_SD_3_VOLTAGE:
  69        case AS3711_SD_4_VOLTAGE:
  70        case AS3711_LDO_1_VOLTAGE:
  71        case AS3711_LDO_2_VOLTAGE:
  72        case AS3711_LDO_3_VOLTAGE:
  73        case AS3711_LDO_4_VOLTAGE:
  74        case AS3711_LDO_5_VOLTAGE:
  75        case AS3711_LDO_6_VOLTAGE:
  76        case AS3711_LDO_7_VOLTAGE:
  77        case AS3711_LDO_8_VOLTAGE:
  78        case AS3711_SD_CONTROL:
  79        case AS3711_GPIO_SIGNAL_OUT:
  80        case AS3711_GPIO_SIGNAL_IN:
  81        case AS3711_SD_CONTROL_1:
  82        case AS3711_SD_CONTROL_2:
  83        case AS3711_CURR_CONTROL:
  84        case AS3711_CURR1_VALUE:
  85        case AS3711_CURR2_VALUE:
  86        case AS3711_CURR3_VALUE:
  87        case AS3711_STEPUP_CONTROL_1:
  88        case AS3711_STEPUP_CONTROL_2:
  89        case AS3711_STEPUP_CONTROL_4:
  90        case AS3711_STEPUP_CONTROL_5:
  91        case AS3711_REG_STATUS:
  92        case AS3711_INTERRUPT_STATUS_1:
  93        case AS3711_INTERRUPT_STATUS_2:
  94        case AS3711_INTERRUPT_STATUS_3:
  95        case AS3711_CHARGER_STATUS_1:
  96        case AS3711_CHARGER_STATUS_2:
  97        case AS3711_ASIC_ID_1:
  98        case AS3711_ASIC_ID_2:
  99                return true;
 100        }
 101        return false;
 102}
 103
 104static const struct regmap_config as3711_regmap_config = {
 105        .reg_bits = 8,
 106        .val_bits = 8,
 107        .volatile_reg = as3711_volatile_reg,
 108        .readable_reg = as3711_readable_reg,
 109        .precious_reg = as3711_precious_reg,
 110        .max_register = AS3711_MAX_REGS,
 111        .num_reg_defaults_raw = AS3711_MAX_REGS,
 112        .cache_type = REGCACHE_RBTREE,
 113};
 114
 115static int as3711_i2c_probe(struct i2c_client *client,
 116                            const struct i2c_device_id *id)
 117{
 118        struct as3711 *as3711;
 119        struct as3711_platform_data *pdata = client->dev.platform_data;
 120        unsigned int id1, id2;
 121        int ret;
 122
 123        if (!pdata)
 124                dev_dbg(&client->dev, "Platform data not found\n");
 125
 126        as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 127        if (!as3711) {
 128                dev_err(&client->dev, "Memory allocation failed\n");
 129                return -ENOMEM;
 130        }
 131
 132        as3711->dev = &client->dev;
 133        i2c_set_clientdata(client, as3711);
 134
 135        if (client->irq)
 136                dev_notice(&client->dev, "IRQ not supported yet\n");
 137
 138        as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
 139        if (IS_ERR(as3711->regmap)) {
 140                ret = PTR_ERR(as3711->regmap);
 141                dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
 142                return ret;
 143        }
 144
 145        ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1);
 146        if (!ret)
 147                ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2);
 148        if (ret < 0) {
 149                dev_err(&client->dev, "regmap_read() failed: %d\n", ret);
 150                return ret;
 151        }
 152        if (id1 != 0x8b)
 153                return -ENODEV;
 154        dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2);
 155
 156        /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */
 157        if (pdata) {
 158                as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator;
 159                as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator);
 160                as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight;
 161                as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight);
 162        } else {
 163                as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
 164                as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
 165                as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL;
 166                as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0;
 167        }
 168
 169        ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs,
 170                              ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL);
 171        if (ret < 0)
 172                dev_err(&client->dev, "add mfd devices failed: %d\n", ret);
 173
 174        return ret;
 175}
 176
 177static int as3711_i2c_remove(struct i2c_client *client)
 178{
 179        struct as3711 *as3711 = i2c_get_clientdata(client);
 180
 181        mfd_remove_devices(as3711->dev);
 182        return 0;
 183}
 184
 185static const struct i2c_device_id as3711_i2c_id[] = {
 186        {.name = "as3711", .driver_data = 0},
 187        {}
 188};
 189
 190MODULE_DEVICE_TABLE(i2c, as3711_i2c_id);
 191
 192static struct i2c_driver as3711_i2c_driver = {
 193        .driver = {
 194                   .name = "as3711",
 195                   .owner = THIS_MODULE,
 196                   },
 197        .probe = as3711_i2c_probe,
 198        .remove = as3711_i2c_remove,
 199        .id_table = as3711_i2c_id,
 200};
 201
 202static int __init as3711_i2c_init(void)
 203{
 204        return i2c_add_driver(&as3711_i2c_driver);
 205}
 206/* Initialise early */
 207subsys_initcall(as3711_i2c_init);
 208
 209static void __exit as3711_i2c_exit(void)
 210{
 211        i2c_del_driver(&as3711_i2c_driver);
 212}
 213module_exit(as3711_i2c_exit);
 214
 215MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
 216MODULE_DESCRIPTION("AS3711 PMIC driver");
 217MODULE_LICENSE("GPL v2");
 218