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