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
 115#ifdef CONFIG_OF
 116static struct of_device_id as3711_of_match[] = {
 117        {.compatible = "ams,as3711",},
 118        {}
 119};
 120MODULE_DEVICE_TABLE(of, as3711_of_match);
 121#endif
 122
 123static int as3711_i2c_probe(struct i2c_client *client,
 124                            const struct i2c_device_id *id)
 125{
 126        struct as3711 *as3711;
 127        struct as3711_platform_data *pdata;
 128        unsigned int id1, id2;
 129        int ret;
 130
 131        if (!client->dev.of_node) {
 132                pdata = client->dev.platform_data;
 133                if (!pdata)
 134                        dev_dbg(&client->dev, "Platform data not found\n");
 135        } else {
 136                pdata = devm_kzalloc(&client->dev,
 137                                     sizeof(*pdata), GFP_KERNEL);
 138                if (!pdata) {
 139                        dev_err(&client->dev, "Failed to allocate pdata\n");
 140                        return -ENOMEM;
 141                }
 142        }
 143
 144        as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 145        if (!as3711) {
 146                dev_err(&client->dev, "Memory allocation failed\n");
 147                return -ENOMEM;
 148        }
 149
 150        as3711->dev = &client->dev;
 151        i2c_set_clientdata(client, as3711);
 152
 153        if (client->irq)
 154                dev_notice(&client->dev, "IRQ not supported yet\n");
 155
 156        as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
 157        if (IS_ERR(as3711->regmap)) {
 158                ret = PTR_ERR(as3711->regmap);
 159                dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
 160                return ret;
 161        }
 162
 163        ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1);
 164        if (!ret)
 165                ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2);
 166        if (ret < 0) {
 167                dev_err(&client->dev, "regmap_read() failed: %d\n", ret);
 168                return ret;
 169        }
 170        if (id1 != 0x8b)
 171                return -ENODEV;
 172        dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2);
 173
 174        /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */
 175        if (pdata) {
 176                as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator;
 177                as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator);
 178                as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight;
 179                as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight);
 180        } else {
 181                as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
 182                as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
 183                as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL;
 184                as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0;
 185        }
 186
 187        ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs,
 188                              ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL);
 189        if (ret < 0)
 190                dev_err(&client->dev, "add mfd devices failed: %d\n", ret);
 191
 192        return ret;
 193}
 194
 195static int as3711_i2c_remove(struct i2c_client *client)
 196{
 197        struct as3711 *as3711 = i2c_get_clientdata(client);
 198
 199        mfd_remove_devices(as3711->dev);
 200        return 0;
 201}
 202
 203static const struct i2c_device_id as3711_i2c_id[] = {
 204        {.name = "as3711", .driver_data = 0},
 205        {}
 206};
 207
 208MODULE_DEVICE_TABLE(i2c, as3711_i2c_id);
 209
 210static struct i2c_driver as3711_i2c_driver = {
 211        .driver = {
 212                   .name = "as3711",
 213                   .owner = THIS_MODULE,
 214                   .of_match_table = of_match_ptr(as3711_of_match),
 215        },
 216        .probe = as3711_i2c_probe,
 217        .remove = as3711_i2c_remove,
 218        .id_table = as3711_i2c_id,
 219};
 220
 221static int __init as3711_i2c_init(void)
 222{
 223        return i2c_add_driver(&as3711_i2c_driver);
 224}
 225/* Initialise early */
 226subsys_initcall(as3711_i2c_init);
 227
 228static void __exit as3711_i2c_exit(void)
 229{
 230        i2c_del_driver(&as3711_i2c_driver);
 231}
 232module_exit(as3711_i2c_exit);
 233
 234MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
 235MODULE_DESCRIPTION("AS3711 PMIC driver");
 236MODULE_LICENSE("GPL v2");
 237