13367ac3eSChiYuan Huang // SPDX-License-Identifier: GPL-2.0+ 23367ac3eSChiYuan Huang /* 33367ac3eSChiYuan Huang * Copyright (c) 2021 Richtek Technology Corp. 43367ac3eSChiYuan Huang * 53367ac3eSChiYuan Huang * Author: ChiYuan Huang <cy_huang@richtek.com> 63367ac3eSChiYuan Huang */ 73367ac3eSChiYuan Huang 83367ac3eSChiYuan Huang #include <linux/gpio/consumer.h> 93367ac3eSChiYuan Huang #include <linux/i2c.h> 103367ac3eSChiYuan Huang #include <linux/kernel.h> 113367ac3eSChiYuan Huang #include <linux/mfd/core.h> 123367ac3eSChiYuan Huang #include <linux/module.h> 133367ac3eSChiYuan Huang #include <linux/regmap.h> 143367ac3eSChiYuan Huang 153367ac3eSChiYuan Huang #define RT4831_REG_REVISION 0x01 163367ac3eSChiYuan Huang #define RT4831_REG_ENABLE 0x08 173367ac3eSChiYuan Huang #define RT4831_REG_I2CPROT 0x15 183367ac3eSChiYuan Huang 193367ac3eSChiYuan Huang #define RICHTEK_VENDOR_ID 0x03 203367ac3eSChiYuan Huang #define RT4831_VID_MASK GENMASK(1, 0) 213367ac3eSChiYuan Huang #define RT4831_RESET_MASK BIT(7) 223367ac3eSChiYuan Huang #define RT4831_I2CSAFETMR_MASK BIT(0) 233367ac3eSChiYuan Huang 243367ac3eSChiYuan Huang static const struct mfd_cell rt4831_subdevs[] = { 253367ac3eSChiYuan Huang MFD_CELL_OF("rt4831-backlight", NULL, NULL, 0, 0, "richtek,rt4831-backlight"), 263367ac3eSChiYuan Huang MFD_CELL_NAME("rt4831-regulator") 273367ac3eSChiYuan Huang }; 283367ac3eSChiYuan Huang 293367ac3eSChiYuan Huang static bool rt4831_is_accessible_reg(struct device *dev, unsigned int reg) 303367ac3eSChiYuan Huang { 313367ac3eSChiYuan Huang if (reg >= RT4831_REG_REVISION && reg <= RT4831_REG_I2CPROT) 323367ac3eSChiYuan Huang return true; 333367ac3eSChiYuan Huang return false; 343367ac3eSChiYuan Huang } 353367ac3eSChiYuan Huang 363367ac3eSChiYuan Huang static const struct regmap_config rt4831_regmap_config = { 373367ac3eSChiYuan Huang .reg_bits = 8, 383367ac3eSChiYuan Huang .val_bits = 8, 393367ac3eSChiYuan Huang .max_register = RT4831_REG_I2CPROT, 403367ac3eSChiYuan Huang 413367ac3eSChiYuan Huang .readable_reg = rt4831_is_accessible_reg, 423367ac3eSChiYuan Huang .writeable_reg = rt4831_is_accessible_reg, 433367ac3eSChiYuan Huang }; 443367ac3eSChiYuan Huang 453367ac3eSChiYuan Huang static int rt4831_probe(struct i2c_client *client) 463367ac3eSChiYuan Huang { 473367ac3eSChiYuan Huang struct gpio_desc *enable_gpio; 483367ac3eSChiYuan Huang struct regmap *regmap; 493367ac3eSChiYuan Huang unsigned int chip_id; 503367ac3eSChiYuan Huang int ret; 513367ac3eSChiYuan Huang 523367ac3eSChiYuan Huang enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH); 533367ac3eSChiYuan Huang if (IS_ERR(enable_gpio)) { 543367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to get 'enable' GPIO\n"); 553367ac3eSChiYuan Huang return PTR_ERR(enable_gpio); 563367ac3eSChiYuan Huang } 573367ac3eSChiYuan Huang 583367ac3eSChiYuan Huang regmap = devm_regmap_init_i2c(client, &rt4831_regmap_config); 593367ac3eSChiYuan Huang if (IS_ERR(regmap)) { 603367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to initialize regmap\n"); 613367ac3eSChiYuan Huang return PTR_ERR(regmap); 623367ac3eSChiYuan Huang } 633367ac3eSChiYuan Huang 643367ac3eSChiYuan Huang ret = regmap_read(regmap, RT4831_REG_REVISION, &chip_id); 653367ac3eSChiYuan Huang if (ret) { 663367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to get H/W revision\n"); 673367ac3eSChiYuan Huang return ret; 683367ac3eSChiYuan Huang } 693367ac3eSChiYuan Huang 703367ac3eSChiYuan Huang if ((chip_id & RT4831_VID_MASK) != RICHTEK_VENDOR_ID) { 713367ac3eSChiYuan Huang dev_err(&client->dev, "Chip vendor ID 0x%02x not matched\n", chip_id); 723367ac3eSChiYuan Huang return -ENODEV; 733367ac3eSChiYuan Huang } 743367ac3eSChiYuan Huang 753367ac3eSChiYuan Huang /* 763367ac3eSChiYuan Huang * Used to prevent the abnormal shutdown. 773367ac3eSChiYuan Huang * If SCL/SDA both keep low for one second to reset HW. 783367ac3eSChiYuan Huang */ 793367ac3eSChiYuan Huang ret = regmap_update_bits(regmap, RT4831_REG_I2CPROT, RT4831_I2CSAFETMR_MASK, 803367ac3eSChiYuan Huang RT4831_I2CSAFETMR_MASK); 813367ac3eSChiYuan Huang if (ret) { 823367ac3eSChiYuan Huang dev_err(&client->dev, "Failed to enable I2C safety timer\n"); 833367ac3eSChiYuan Huang return ret; 843367ac3eSChiYuan Huang } 853367ac3eSChiYuan Huang 863367ac3eSChiYuan Huang return devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, rt4831_subdevs, 873367ac3eSChiYuan Huang ARRAY_SIZE(rt4831_subdevs), NULL, 0, NULL); 883367ac3eSChiYuan Huang } 893367ac3eSChiYuan Huang 90*ed5c2f5fSUwe Kleine-König static void rt4831_remove(struct i2c_client *client) 913367ac3eSChiYuan Huang { 923367ac3eSChiYuan Huang struct regmap *regmap = dev_get_regmap(&client->dev, NULL); 93ade0642dSUwe Kleine-König int ret; 943367ac3eSChiYuan Huang 953367ac3eSChiYuan Huang /* Disable WLED and DSV outputs */ 96ade0642dSUwe Kleine-König ret = regmap_update_bits(regmap, RT4831_REG_ENABLE, RT4831_RESET_MASK, RT4831_RESET_MASK); 97ade0642dSUwe Kleine-König if (ret) 98ade0642dSUwe Kleine-König dev_warn(&client->dev, "Failed to disable outputs (%pe)\n", ERR_PTR(ret)); 993367ac3eSChiYuan Huang } 1003367ac3eSChiYuan Huang 1013367ac3eSChiYuan Huang static const struct of_device_id __maybe_unused rt4831_of_match[] = { 1023367ac3eSChiYuan Huang { .compatible = "richtek,rt4831", }, 1033367ac3eSChiYuan Huang {} 1043367ac3eSChiYuan Huang }; 1053367ac3eSChiYuan Huang MODULE_DEVICE_TABLE(of, rt4831_of_match); 1063367ac3eSChiYuan Huang 1073367ac3eSChiYuan Huang static struct i2c_driver rt4831_driver = { 1083367ac3eSChiYuan Huang .driver = { 1093367ac3eSChiYuan Huang .name = "rt4831", 1103367ac3eSChiYuan Huang .of_match_table = rt4831_of_match, 1113367ac3eSChiYuan Huang }, 1123367ac3eSChiYuan Huang .probe_new = rt4831_probe, 1133367ac3eSChiYuan Huang .remove = rt4831_remove, 1143367ac3eSChiYuan Huang }; 1153367ac3eSChiYuan Huang module_i2c_driver(rt4831_driver); 1163367ac3eSChiYuan Huang 1173367ac3eSChiYuan Huang MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 1183367ac3eSChiYuan Huang MODULE_LICENSE("GPL v2"); 119