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