18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AS3711 PMIC MFC driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/as3711.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cienum { 218c2ecf20Sopenharmony_ci AS3711_REGULATOR, 228c2ecf20Sopenharmony_ci AS3711_BACKLIGHT, 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Ok to have it static: it is only used during probing and multiple I2C devices 278c2ecf20Sopenharmony_ci * cannot be probed simultaneously. Just make sure to avoid stale data. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic struct mfd_cell as3711_subdevs[] = { 308c2ecf20Sopenharmony_ci [AS3711_REGULATOR] = {.name = "as3711-regulator",}, 318c2ecf20Sopenharmony_ci [AS3711_BACKLIGHT] = {.name = "as3711-backlight",}, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic bool as3711_volatile_reg(struct device *dev, unsigned int reg) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci switch (reg) { 378c2ecf20Sopenharmony_ci case AS3711_GPIO_SIGNAL_IN: 388c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_1: 398c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_2: 408c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_3: 418c2ecf20Sopenharmony_ci case AS3711_CHARGER_STATUS_1: 428c2ecf20Sopenharmony_ci case AS3711_CHARGER_STATUS_2: 438c2ecf20Sopenharmony_ci case AS3711_REG_STATUS: 448c2ecf20Sopenharmony_ci return true; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic bool as3711_precious_reg(struct device *dev, unsigned int reg) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci switch (reg) { 528c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_1: 538c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_2: 548c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_3: 558c2ecf20Sopenharmony_ci return true; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci return false; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic bool as3711_readable_reg(struct device *dev, unsigned int reg) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci switch (reg) { 638c2ecf20Sopenharmony_ci case AS3711_SD_1_VOLTAGE: 648c2ecf20Sopenharmony_ci case AS3711_SD_2_VOLTAGE: 658c2ecf20Sopenharmony_ci case AS3711_SD_3_VOLTAGE: 668c2ecf20Sopenharmony_ci case AS3711_SD_4_VOLTAGE: 678c2ecf20Sopenharmony_ci case AS3711_LDO_1_VOLTAGE: 688c2ecf20Sopenharmony_ci case AS3711_LDO_2_VOLTAGE: 698c2ecf20Sopenharmony_ci case AS3711_LDO_3_VOLTAGE: 708c2ecf20Sopenharmony_ci case AS3711_LDO_4_VOLTAGE: 718c2ecf20Sopenharmony_ci case AS3711_LDO_5_VOLTAGE: 728c2ecf20Sopenharmony_ci case AS3711_LDO_6_VOLTAGE: 738c2ecf20Sopenharmony_ci case AS3711_LDO_7_VOLTAGE: 748c2ecf20Sopenharmony_ci case AS3711_LDO_8_VOLTAGE: 758c2ecf20Sopenharmony_ci case AS3711_SD_CONTROL: 768c2ecf20Sopenharmony_ci case AS3711_GPIO_SIGNAL_OUT: 778c2ecf20Sopenharmony_ci case AS3711_GPIO_SIGNAL_IN: 788c2ecf20Sopenharmony_ci case AS3711_SD_CONTROL_1: 798c2ecf20Sopenharmony_ci case AS3711_SD_CONTROL_2: 808c2ecf20Sopenharmony_ci case AS3711_CURR_CONTROL: 818c2ecf20Sopenharmony_ci case AS3711_CURR1_VALUE: 828c2ecf20Sopenharmony_ci case AS3711_CURR2_VALUE: 838c2ecf20Sopenharmony_ci case AS3711_CURR3_VALUE: 848c2ecf20Sopenharmony_ci case AS3711_STEPUP_CONTROL_1: 858c2ecf20Sopenharmony_ci case AS3711_STEPUP_CONTROL_2: 868c2ecf20Sopenharmony_ci case AS3711_STEPUP_CONTROL_4: 878c2ecf20Sopenharmony_ci case AS3711_STEPUP_CONTROL_5: 888c2ecf20Sopenharmony_ci case AS3711_REG_STATUS: 898c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_1: 908c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_2: 918c2ecf20Sopenharmony_ci case AS3711_INTERRUPT_STATUS_3: 928c2ecf20Sopenharmony_ci case AS3711_CHARGER_STATUS_1: 938c2ecf20Sopenharmony_ci case AS3711_CHARGER_STATUS_2: 948c2ecf20Sopenharmony_ci case AS3711_ASIC_ID_1: 958c2ecf20Sopenharmony_ci case AS3711_ASIC_ID_2: 968c2ecf20Sopenharmony_ci return true; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return false; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic const struct regmap_config as3711_regmap_config = { 1028c2ecf20Sopenharmony_ci .reg_bits = 8, 1038c2ecf20Sopenharmony_ci .val_bits = 8, 1048c2ecf20Sopenharmony_ci .volatile_reg = as3711_volatile_reg, 1058c2ecf20Sopenharmony_ci .readable_reg = as3711_readable_reg, 1068c2ecf20Sopenharmony_ci .precious_reg = as3711_precious_reg, 1078c2ecf20Sopenharmony_ci .max_register = AS3711_MAX_REG, 1088c2ecf20Sopenharmony_ci .num_reg_defaults_raw = AS3711_NUM_REGS, 1098c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1138c2ecf20Sopenharmony_cistatic const struct of_device_id as3711_of_match[] = { 1148c2ecf20Sopenharmony_ci {.compatible = "ams,as3711",}, 1158c2ecf20Sopenharmony_ci {} 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci#endif 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int as3711_i2c_probe(struct i2c_client *client, 1208c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct as3711 *as3711; 1238c2ecf20Sopenharmony_ci struct as3711_platform_data *pdata; 1248c2ecf20Sopenharmony_ci unsigned int id1, id2; 1258c2ecf20Sopenharmony_ci int ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!client->dev.of_node) { 1288c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&client->dev); 1298c2ecf20Sopenharmony_ci if (!pdata) 1308c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Platform data not found\n"); 1318c2ecf20Sopenharmony_ci } else { 1328c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&client->dev, 1338c2ecf20Sopenharmony_ci sizeof(*pdata), GFP_KERNEL); 1348c2ecf20Sopenharmony_ci if (!pdata) 1358c2ecf20Sopenharmony_ci return -ENOMEM; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL); 1398c2ecf20Sopenharmony_ci if (!as3711) 1408c2ecf20Sopenharmony_ci return -ENOMEM; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci as3711->dev = &client->dev; 1438c2ecf20Sopenharmony_ci i2c_set_clientdata(client, as3711); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (client->irq) 1468c2ecf20Sopenharmony_ci dev_notice(&client->dev, "IRQ not supported yet\n"); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config); 1498c2ecf20Sopenharmony_ci if (IS_ERR(as3711->regmap)) { 1508c2ecf20Sopenharmony_ci ret = PTR_ERR(as3711->regmap); 1518c2ecf20Sopenharmony_ci dev_err(&client->dev, 1528c2ecf20Sopenharmony_ci "regmap initialization failed: %d\n", ret); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1); 1578c2ecf20Sopenharmony_ci if (!ret) 1588c2ecf20Sopenharmony_ci ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2); 1598c2ecf20Sopenharmony_ci if (ret < 0) { 1608c2ecf20Sopenharmony_ci dev_err(&client->dev, "regmap_read() failed: %d\n", ret); 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci if (id1 != 0x8b) 1648c2ecf20Sopenharmony_ci return -ENODEV; 1658c2ecf20Sopenharmony_ci dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * We can reuse as3711_subdevs[], 1698c2ecf20Sopenharmony_ci * it will be copied in mfd_add_devices() 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci if (pdata) { 1728c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_REGULATOR].platform_data = 1738c2ecf20Sopenharmony_ci &pdata->regulator; 1748c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_REGULATOR].pdata_size = 1758c2ecf20Sopenharmony_ci sizeof(pdata->regulator); 1768c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_BACKLIGHT].platform_data = 1778c2ecf20Sopenharmony_ci &pdata->backlight; 1788c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 1798c2ecf20Sopenharmony_ci sizeof(pdata->backlight); 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_REGULATOR].platform_data = NULL; 1828c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_REGULATOR].pdata_size = 0; 1838c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL; 1848c2ecf20Sopenharmony_ci as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ret = devm_mfd_add_devices(as3711->dev, -1, as3711_subdevs, 1888c2ecf20Sopenharmony_ci ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL); 1898c2ecf20Sopenharmony_ci if (ret < 0) 1908c2ecf20Sopenharmony_ci dev_err(&client->dev, "add mfd devices failed: %d\n", ret); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct i2c_device_id as3711_i2c_id[] = { 1968c2ecf20Sopenharmony_ci {.name = "as3711", .driver_data = 0}, 1978c2ecf20Sopenharmony_ci {} 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic struct i2c_driver as3711_i2c_driver = { 2018c2ecf20Sopenharmony_ci .driver = { 2028c2ecf20Sopenharmony_ci .name = "as3711", 2038c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(as3711_of_match), 2048c2ecf20Sopenharmony_ci }, 2058c2ecf20Sopenharmony_ci .probe = as3711_i2c_probe, 2068c2ecf20Sopenharmony_ci .id_table = as3711_i2c_id, 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int __init as3711_i2c_init(void) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci return i2c_add_driver(&as3711_i2c_driver); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci/* Initialise early */ 2148c2ecf20Sopenharmony_cisubsys_initcall(as3711_i2c_init); 215