1*3367ac3eSChiYuan Huang // SPDX-License-Identifier: GPL-2.0+ 2*3367ac3eSChiYuan Huang /* 3*3367ac3eSChiYuan Huang * Copyright (c) 2021 Richtek Technology Corp. 4*3367ac3eSChiYuan Huang * 5*3367ac3eSChiYuan Huang * Author: ChiYuan Huang <cy_huang@richtek.com> 6*3367ac3eSChiYuan Huang */ 7*3367ac3eSChiYuan Huang 8*3367ac3eSChiYuan Huang #include <linux/gpio/consumer.h> 9*3367ac3eSChiYuan Huang #include <linux/i2c.h> 10*3367ac3eSChiYuan Huang #include <linux/kernel.h> 11*3367ac3eSChiYuan Huang #include <linux/mfd/core.h> 12*3367ac3eSChiYuan Huang #include <linux/module.h> 13*3367ac3eSChiYuan Huang #include <linux/regmap.h> 14*3367ac3eSChiYuan Huang 15*3367ac3eSChiYuan Huang #define RT4831_REG_REVISION 0x01 16*3367ac3eSChiYuan Huang #define RT4831_REG_ENABLE 0x08 17*3367ac3eSChiYuan Huang #define RT4831_REG_I2CPROT 0x15 18*3367ac3eSChiYuan Huang 19*3367ac3eSChiYuan Huang #define RICHTEK_VENDOR_ID 0x03 20*3367ac3eSChiYuan Huang #define RT4831_VID_MASK GENMASK(1, 0) 21*3367ac3eSChiYuan Huang #define RT4831_RESET_MASK BIT(7) 22*3367ac3eSChiYuan Huang #define RT4831_I2CSAFETMR_MASK BIT(0) 23*3367ac3eSChiYuan Huang 24*3367ac3eSChiYuan Huang static const struct mfd_cell rt4831_subdevs[] = { 25*3367ac3eSChiYuan Huang MFD_CELL_OF("rt4831-backlight", NULL, NULL, 0, 0, "richtek,rt4831-backlight"), 26*3367ac3eSChiYuan Huang MFD_CELL_NAME("rt4831-regulator") 27*3367ac3eSChiYuan Huang }; 28*3367ac3eSChiYuan Huang 29*3367ac3eSChiYuan Huang static bool rt4831_is_accessible_reg(struct device *dev, unsigned int reg) 30*3367ac3eSChiYuan Huang { 31*3367ac3eSChiYuan Huang if (reg >= RT4831_REG_REVISION && reg <= RT4831_REG_I2CPROT) 32*3367ac3eSChiYuan Huang return true; 33*3367ac3eSChiYuan Huang return false; 34*3367ac3eSChiYuan Huang } 35*3367ac3eSChiYuan Huang 36*3367ac3eSChiYuan Huang static const struct regmap_config rt4831_regmap_config = { 37*3367ac3eSChiYuan Huang .reg_bits = 8, 38*3367ac3eSChiYuan Huang .val_bits = 8, 39*3367ac3eSChiYuan Huang .max_register = RT4831_REG_I2CPROT, 40*3367ac3eSChiYuan Huang 41*3367ac3eSChiYuan Huang .readable_reg = rt4831_is_accessible_reg, 42*3367ac3eSChiYuan Huang .writeable_reg = rt4831_is_accessible_reg, 43*3367ac3eSChiYuan Huang }; 44*3367ac3eSChiYuan Huang 45*3367ac3eSChiYuan Huang static int rt4831_probe(struct i2c_client *client) 46*3367ac3eSChiYuan Huang { 47*3367ac3eSChiYuan Huang struct gpio_desc *enable_gpio; 48*3367ac3eSChiYuan Huang struct regmap *regmap; 49*3367ac3eSChiYuan Huang unsigned int chip_id; 50*3367ac3eSChiYuan Huang int ret; 51*3367ac3eSChiYuan Huang 52*3367ac3eSChiYuan Huang enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH); 53*3367ac3eSChiYuan Huang if (IS_ERR(enable_gpio)) { 54*3367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to get 'enable' GPIO\n"); 55*3367ac3eSChiYuan Huang return PTR_ERR(enable_gpio); 56*3367ac3eSChiYuan Huang } 57*3367ac3eSChiYuan Huang 58*3367ac3eSChiYuan Huang regmap = devm_regmap_init_i2c(client, &rt4831_regmap_config); 59*3367ac3eSChiYuan Huang if (IS_ERR(regmap)) { 60*3367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to initialize regmap\n"); 61*3367ac3eSChiYuan Huang return PTR_ERR(regmap); 62*3367ac3eSChiYuan Huang } 63*3367ac3eSChiYuan Huang 64*3367ac3eSChiYuan Huang ret = regmap_read(regmap, RT4831_REG_REVISION, &chip_id); 65*3367ac3eSChiYuan Huang if (ret) { 66*3367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to get H/W revision\n"); 67*3367ac3eSChiYuan Huang return ret; 68*3367ac3eSChiYuan Huang } 69*3367ac3eSChiYuan Huang 70*3367ac3eSChiYuan Huang if ((chip_id & RT4831_VID_MASK) != RICHTEK_VENDOR_ID) { 71*3367ac3eSChiYuan Huang dev_err(&client->dev, "Chip vendor ID 0x%02x not matched\n", chip_id); 72*3367ac3eSChiYuan Huang return -ENODEV; 73*3367ac3eSChiYuan Huang } 74*3367ac3eSChiYuan Huang 75*3367ac3eSChiYuan Huang /* 76*3367ac3eSChiYuan Huang * Used to prevent the abnormal shutdown. 77*3367ac3eSChiYuan Huang * If SCL/SDA both keep low for one second to reset HW. 78*3367ac3eSChiYuan Huang */ 79*3367ac3eSChiYuan Huang ret = regmap_update_bits(regmap, RT4831_REG_I2CPROT, RT4831_I2CSAFETMR_MASK, 80*3367ac3eSChiYuan Huang RT4831_I2CSAFETMR_MASK); 81*3367ac3eSChiYuan Huang if (ret) { 82*3367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to enable I2C safety timer\n"); 83*3367ac3eSChiYuan Huang return ret; 84*3367ac3eSChiYuan Huang } 85*3367ac3eSChiYuan Huang 86*3367ac3eSChiYuan Huang return devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, rt4831_subdevs, 87*3367ac3eSChiYuan Huang ARRAY_SIZE(rt4831_subdevs), NULL, 0, NULL); 88*3367ac3eSChiYuan Huang } 89*3367ac3eSChiYuan Huang 90*3367ac3eSChiYuan Huang static int rt4831_remove(struct i2c_client *client) 91*3367ac3eSChiYuan Huang { 92*3367ac3eSChiYuan Huang struct regmap *regmap = dev_get_regmap(&client->dev, NULL); 93*3367ac3eSChiYuan Huang 94*3367ac3eSChiYuan Huang /* Disable WLED and DSV outputs */ 95*3367ac3eSChiYuan Huang return regmap_update_bits(regmap, RT4831_REG_ENABLE, RT4831_RESET_MASK, RT4831_RESET_MASK); 96*3367ac3eSChiYuan Huang } 97*3367ac3eSChiYuan Huang 98*3367ac3eSChiYuan Huang static const struct of_device_id __maybe_unused rt4831_of_match[] = { 99*3367ac3eSChiYuan Huang { .compatible = "richtek,rt4831", }, 100*3367ac3eSChiYuan Huang {} 101*3367ac3eSChiYuan Huang }; 102*3367ac3eSChiYuan Huang MODULE_DEVICE_TABLE(of, rt4831_of_match); 103*3367ac3eSChiYuan Huang 104*3367ac3eSChiYuan Huang static struct i2c_driver rt4831_driver = { 105*3367ac3eSChiYuan Huang .driver = { 106*3367ac3eSChiYuan Huang .name = "rt4831", 107*3367ac3eSChiYuan Huang .of_match_table = rt4831_of_match, 108*3367ac3eSChiYuan Huang }, 109*3367ac3eSChiYuan Huang .probe_new = rt4831_probe, 110*3367ac3eSChiYuan Huang .remove = rt4831_remove, 111*3367ac3eSChiYuan Huang }; 112*3367ac3eSChiYuan Huang module_i2c_driver(rt4831_driver); 113*3367ac3eSChiYuan Huang 114*3367ac3eSChiYuan Huang MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 115*3367ac3eSChiYuan Huang MODULE_LICENSE("GPL v2"); 116