as3711.c (5392B)
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);