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