/* * int34d9-regulator.c - Puma7 PMIC regulator driver * * Copyright (c) 2020-2022 MaxLinear, Inc. * Copyright (c) 2008-2020 Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* RESETSRC0 - RESET SOURCE REGISTER 0 * This register contains the cause of a shutdown or reset, on start up. */ #define PUMA7_PMIC_SHUTDOWN_REASON_REG 0x20 /* RCVCFG - Recovery Configuration Register * D[7:4] Limit number of attempts to recover the system after a VR Fault * 0: no recovery * 14: 14 times * 15: No limit of attempts to recover */ #define PUMA7_PMIC_RECOVERY_CFG_REG 0x6F /* RCVNUM - Recovery Number Register * Number of attempts to recover the system after a VR Fault occurred. * Only bits [3:0] are changeable. * Bits [7:4] are reserved. */ #define PUMA7_PMIC_RECOVER_COUNT_REG 0x70 /* Holtek's VersionNumber is placed in register 0xF1 @page0 (EEPROM-emulator), * which I2C-Address is 0x5E. * Note: This Register is not specified in pmic datasheet. It was added by MPS. * Ref: See SourceCode of Holtek's firmware. */ #define PUMA7_PMIC_HOLTEK_FW_VERSION_REG 0xF1 /* PMIC_EEPROM_ADDRESS * PMIC has four different I2C-slave addresses. * See datasheet BD2651 (Rohm) for more details. */ #define PUMA7_PMIC_PAGE0_ADDR 0x5E /*PMIC EEPROM*/ #define PUMA7_PMIC_PAGE1_ADDR 0x6E /*PMIC PMIC*/ #define PUMA7_PMIC_PAGE2_ADDR 0x6C /*PMIC VCC0*/ #define PUMA7_PMIC_PAGE3_ADDR 0x6D /*PMIC VNN*/ static struct avm_hwrev_info avm_hw_info; static int puma7_pmic_lpm_bbu = PUMA7_PMIC_LPM_CFG_BBU; static int puma7_pmic_lpm_sb = PUMA7_PMIC_LPM_CFG_FAST_STANDBY; static int puma7_pmic_lpm_dsb = PUMA7_PMIC_LPM_CFG_DEEP_STANDBY; module_param(puma7_pmic_lpm_bbu, int, 0644); MODULE_PARM_DESC(puma7_pmic_lpm_bbu, "BBU Low Power Mode configuration"); module_param(puma7_pmic_lpm_sb, int, 0644); MODULE_PARM_DESC(puma7_pmic_lpm_sb, "Standby Low Power Mode configuration"); module_param(puma7_pmic_lpm_dsb, int, 0644); MODULE_PARM_DESC(puma7_pmic_lpm_dsb, "Deep Standby Low Power Mode configuration"); static DEFINE_MUTEX(puma7_pmic_mutex); static s32 pmic_i2c_client_read(struct i2c_client *client, u8 reg) { s32 ret_val; mutex_lock(&puma7_pmic_mutex); ret_val = i2c_smbus_read_byte_data(client, reg); mutex_unlock(&puma7_pmic_mutex); return ret_val; } static s32 pmic_i2c_client_write(struct i2c_client *client, u8 reg, u8 val) { s32 ret_val; mutex_lock(&puma7_pmic_mutex); ret_val = i2c_smbus_write_byte_data(client, reg, val); mutex_unlock(&puma7_pmic_mutex); return ret_val; } static s32 puma7_pmic_read(struct puma7_pmic *pmic, u8 reg) { return pmic_i2c_client_read(pmic->client, reg); } static s32 puma7_pmic_write(struct puma7_pmic *pmic, u8 reg, u8 val) { return pmic_i2c_client_write(pmic->client, reg, val); } struct latched { uint8_t value; }; struct pmic_sysfs_reg_attr { unsigned int reg; unsigned int shift; unsigned int nbits; enum PMIC_PAGES page; struct device_attribute attr; struct latched latched; }; #define to_pmic_reg_attr(_attr) \ container_of(_attr, struct pmic_sysfs_reg_attr, attr) static struct i2c_client *pmic_i2c_client(struct puma7_pmic *pmic, enum PMIC_PAGES page) { if (page == PMIC_PAGE_MAIN) return pmic->client; return pmic->pages[page]; } static ssize_t pmic_latched_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pmic_sysfs_reg_attr *reginfo = to_pmic_reg_attr(attr); return sprintf(buf, "%d\n", reginfo->latched.value); } static ssize_t pmic_latched_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pmic_sysfs_reg_attr *reginfo; s32 ret; u8 val; reginfo = to_pmic_reg_attr(attr); ret = kstrtou8(buf, 0, &val); if (ret) return ret; reginfo->latched.value = ret; return ret ?: count; } #define PMIC_REG_ATTR_LATCHED_BITS(_name, _page, _reg, _shift, _nbits) {\ .attr = __ATTR(_name, 0644, pmic_latched_show, \ pmic_latched_store), \ .page = _page, \ .reg = _reg, \ .shift = _shift, \ .nbits = _nbits, \ } #define PMIC_REG_ATTR_LATCHED(_name, _page, _reg) \ PMIC_REG_ATTR_LATCHED_BITS(_name, _page, _reg, 0, 8) static struct pmic_sysfs_reg_attr sysfs_reg_attr[] = { PMIC_REG_ATTR_LATCHED(RESETSRC0, PMIC_PAGE_MAIN, PUMA7_PMIC_REG_RESETSRC0), PMIC_REG_ATTR_LATCHED(WAKESRC, PMIC_PAGE_MAIN, PUMA7_PMIC_REG_WAKESRC), PMIC_REG_ATTR_LATCHED(RCVCFG, PMIC_PAGE_MAIN, PUMA7_PMIC_REG_RCVCFG), PMIC_REG_ATTR_LATCHED(RCVNUM, PMIC_PAGE_MAIN, PUMA7_PMIC_REG_RCVNUM), PMIC_REG_ATTR_LATCHED(HOLTEK_VRS, PMIC_PAGE0, PUMA7_PMIC_HOLTEK_FW_VERSION_REG), }; static int pmic_sysfs_genattrs(struct device *dev) { struct pmic_sysfs_reg_attr *attr; int i, ret; for (i = 0; i < ARRAY_SIZE(sysfs_reg_attr); ++i) { attr = &sysfs_reg_attr[i]; ret = device_create_file(dev, &attr->attr); if (ret) goto err_cleanup; } return 0; err_cleanup: while (i-- > 0) { attr = &sysfs_reg_attr[i]; device_remove_file(dev, &attr->attr); } return ret; } static void pmic_sysfs_remove_attrs(struct device *dev) { struct pmic_sysfs_reg_attr *attr; int i; for (i = 0; i < ARRAY_SIZE(sysfs_reg_attr); ++i) { attr = &sysfs_reg_attr[i]; device_remove_file(dev, &attr->attr); } } static int puma7_pmic_init_i2c_dummys(struct puma7_pmic *pmic) { int i; pmic->pages[PMIC_PAGE0] = i2c_new_dummy_device(pmic->client->adapter, PUMA7_PMIC_PAGE0_ADDR); pmic->pages[PMIC_PAGE2] = i2c_new_dummy_device(pmic->client->adapter, PUMA7_PMIC_PAGE2_ADDR); pmic->pages[PMIC_PAGE3] = i2c_new_dummy_device(pmic->client->adapter, PUMA7_PMIC_PAGE3_ADDR); for (i = 0; i < PMIC_EXTRA_PAGECNT; i++) { if (IS_ERR(pmic->pages[i])) goto err_page; } return 0; err_page: for (i = 0; i < PMIC_EXTRA_PAGECNT; i++) { if (!IS_ERR(pmic->pages[i])) i2c_unregister_device(pmic->pages[i]); } return -ENOMEM; } static int puma7_pmic_regulator_config(struct puma7_pmic *pmic) { s32 ret; u8 reg_val = 0x0F; /*fix value to set*/ u8 val; ret = puma7_pmic_read(pmic, PUMA7_PMIC_REG_RCVCFG); if (ret < 0) return ret; val = ret&0x0F; val |= (reg_val<<4); ret = puma7_pmic_write(pmic, PUMA7_PMIC_REG_RCVCFG, val); if (ret < 0) return ret; return 0; } static u8 puma7_latch_reg_val(struct puma7_pmic *pmic, struct pmic_sysfs_reg_attr *sysfs) { struct i2c_client *client = pmic_i2c_client(pmic, sysfs->page); s32 val; val = pmic_i2c_client_read(client, sysfs->reg); if (val < 0) return val; sysfs->latched.value = val; return 0; } static int puma7_pmic_regulator_read_once(struct puma7_pmic *pmic) { s32 ret; int i; for (i = 0; i < ARRAY_SIZE(sysfs_reg_attr); i++) { puma7_latch_reg_val(pmic, &sysfs_reg_attr[i]); if ((sysfs_reg_attr[i].reg == PUMA7_PMIC_REG_RESETSRC0) || (sysfs_reg_attr[i].reg == PUMA7_PMIC_REG_WAKESRC)) { ret = pmic_i2c_client_write( pmic_i2c_client(pmic, sysfs_reg_attr[i].page), sysfs_reg_attr[i].reg, 0x00); /*clear value*/ if (ret < 0) return ret; } } return 0; } static int puma7_pmic_regulator_is_enabled(struct regulator_dev *rdev) { struct puma7_pmic *pmic = rdev_get_drvdata(rdev); u32 mask = rdev->desc->enable_mask; u8 reg = (u8)rdev->desc->enable_reg; s32 reg_val; reg_val = puma7_pmic_read(pmic, reg); if(reg_val < 0) { dev_err(&pmic->client->dev, "PMIC read failed in is regulator enabled!\n"); return reg_val; } return ((reg_val & mask)? 0 : 1); } static s32 puma7_pmic_regulator_enable(struct regulator_dev *rdev) { struct puma7_pmic *pmic = rdev_get_drvdata(rdev); u32 mask = rdev->desc->enable_mask; u8 reg = (u8)rdev->desc->enable_reg; s32 reg_val; s32 ret_val; reg_val = puma7_pmic_read(pmic, reg); if(reg_val < 0) { dev_err(&pmic->client->dev, "PMIC read failed in regulator enable!\n"); return reg_val; } reg_val = reg_val & ~mask; ret_val = puma7_pmic_write(pmic, reg, reg_val); if(ret_val < 0) { dev_err(&pmic->client->dev, "PMIC write failed in regulator enable!\n"); } return ret_val; } static int puma7_pmic_regulator_disable(struct regulator_dev *rdev) { struct puma7_pmic *pmic = rdev_get_drvdata(rdev); u32 mask = rdev->desc->enable_mask; u8 reg = (u8)rdev->desc->enable_reg; s32 reg_val, ret_val; reg_val = puma7_pmic_read(pmic, reg); if(reg_val < 0) { dev_err(&pmic->client->dev, "PMIC read failed in regulator disable!\n"); return reg_val; } reg_val = reg_val | mask; ret_val = puma7_pmic_write(pmic, reg, reg_val); if(ret_val < 0) { dev_err(&pmic->client->dev, "PMIC write failed in regulator disable!\n"); } return ret_val; } static int puma7_pmic_set_lpm_mode(struct regulator_dev *rdev, unsigned int mode) { struct puma7_pmic *pmic = rdev_get_drvdata(rdev); u8 reg_val; s32 ret_val; switch (mode) { case PUMA7_PMIC_MODE_ACTIVE: reg_val = PUMA7_PMIC_LPM_CFG_ACTIVE; break; case PUMA7_PMIC_MODE_BBU: reg_val = puma7_pmic_lpm_bbu & 0xFF; break; case PUMA7_PMIC_MODE_FAST_STANDBY: reg_val = puma7_pmic_lpm_sb & 0xFF; break; case PUMA7_PMIC_MODE_DEEP_STANDBY: reg_val = puma7_pmic_lpm_dsb & 0xFF; break; default: return -EINVAL; } ret_val = puma7_pmic_write(pmic, PUMA7_PMIC_LPMCTRL_REG, reg_val); if(ret_val < 0) { dev_err(&pmic->client->dev, "PMIC write failed while setting lpm mode!\n"); } else { pmic->mode = mode; } return ret_val; } static unsigned int puma7_pmic_get_lpm_mode(struct regulator_dev *rdev) { struct puma7_pmic *pmic = rdev_get_drvdata(rdev); return pmic->mode; } static struct regulator_ops puma7_pmic_regulator_ops = { .is_enabled = puma7_pmic_regulator_is_enabled, .enable = puma7_pmic_regulator_enable, .disable = puma7_pmic_regulator_disable, }; static struct regulator_ops puma7_pmic_ops = { .is_enabled = puma7_pmic_regulator_is_enabled, .enable = puma7_pmic_regulator_enable, .disable = puma7_pmic_regulator_disable, .set_mode = puma7_pmic_set_lpm_mode, .get_mode = puma7_pmic_get_lpm_mode, }; static const struct regulator_desc puma7_pmic_reg[] = { { .name = "VPLT_DCDC", .id = PUMA7_PMIC_VPLT, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_VPLT_EN_MASK, }, { .name = "VCC0_VID_BUCK", .id = PUMA7_PMIC_VCC0, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_VCC0_EN_MASK, }, { .name = "V3P3_EXT_DCDC", .id = PUMA7_PMIC_V3P3_EXT, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_V3P3_EXT_EN_MASK, }, { .name = "VNN_VID_BUCK", .id = PUMA7_PMIC_VNN, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_VNN_EN_MASK, }, { .name = "V1P15_BUCK", .id = PUMA7_PMIC_V1P15, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_V1P15_EN_MASK, }, { .name = "V1P05A_BUCK", .id = PUMA7_PMIC_V1P05A, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_V1P05A_EN_MASK, }, { .name = "VDDQ_BUCK", .id = PUMA7_PMIC_VDDQ, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_VDDQ_EN_MASK, }, { .name = "V1P8A_BUCK", .id = PUMA7_PMIC_V1P8A, .ops = &puma7_pmic_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_V1P8A_EN_MASK, }, { .name = "PUMA7_PMIC", .id = PUMA7_PMIC, .ops = &puma7_pmic_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = PUMA7_PMIC_LPMCTRL_REG, .enable_mask = PUMA7_PMIC_EN_MASK, }, }; int puma7_pmic_regulator_init(void *driver_data) { struct puma7_pmic *pmic = driver_data; static int num_subdev = 0; struct puma7_pmic_subdev_data *sub = pmic->platform_data.subdevs; int ret = 0; sub = sub + num_subdev; if(num_subdev < PUMA7_PMIC_SUPPLY_NUMBER) { printk(KERN_INFO "Puma7 PMIC %s regulator %d initialised\n", sub->name, sub->id); sub->regulator_data.constraints.always_on = 1; if(sub->id == PUMA7_PMIC) { sub->regulator_data.constraints.valid_modes_mask = ( PUMA7_PMIC_MODE_ACTIVE | PUMA7_PMIC_MODE_BBU | PUMA7_PMIC_MODE_FAST_STANDBY | PUMA7_PMIC_MODE_DEEP_STANDBY ); sub->regulator_data.constraints.valid_ops_mask = REGULATOR_CHANGE_MODE; } num_subdev++; } else { ret = -EINVAL; } return ret; } static int puma7_pmic_ac_dc_indication(struct puma7_pmic *pmic, int ac_dc) { int ret = 0; struct acpi_object_list input; union acpi_object param; acpi_status status; param.type = ACPI_TYPE_INTEGER; param.integer.value = ac_dc; input.count = 1; input.pointer = ¶m; status = acpi_evaluate_object(pmic->handle, "PADI", &input, NULL); if (!ACPI_SUCCESS(status)) { dev_err(&pmic->client->dev, "Puma ACPI PADI method execution failed!\n"); ret = -EINVAL; } return ret; } /* PMIC interrupt handler */ static irqreturn_t puma7_pmic_isr(int irq, void *data) { struct puma7_pmic *pmic = data; s32 pwr_src; s32 reg_val; reg_val = puma7_pmic_read(pmic, PUMA7_PMIC_IRQLVL1_REG); if(reg_val < 0) { dev_err(&pmic->client->dev, "PMIC IRQ reg read failed!\n"); return IRQ_NONE; } else if ( reg_val & PUMA7_PMIC_IRQLVL1_THRM) { acpi_bus_generate_netlink_event(ACPI_PUMA7_PMIC_CLASS, ACPI_PUMA7_PMIC_DEVICE_NAME, PUMA7_PMIC_THERMAL_NOTIFY_CRITICAL, 0); reg_val = puma7_pmic_write(pmic, PUMA7_PMIC_THRMIRQ_REG, PUMA7_PMIC_THRMIRQ_PMICHOT); if (reg_val < 0) { dev_err(&pmic->client->dev, "PMIC THERMIRQ reg write failed!\n"); return IRQ_NONE; } } else if ( reg_val & PUMA7_PMIC_IRQLVL1_PWRSRC) { reg_val = puma7_pmic_read(pmic, PUMA7_PMIC_SPWRSRC_REG); if (reg_val < 0) { dev_err(&pmic->client->dev, "PMIC SPWRSRC reg read failed!\n"); return IRQ_NONE; } pwr_src = (reg_val & PUMA7_PMIC_SPWRSRC_SDCINDET) ? 1 : 0; // 0 = Disconnected 1 = Connected if ((puma7_pmic_ac_dc_indication(pmic, pwr_src)) < 0) { dev_err(&pmic->client->dev, "PMIC ac dc indication error!\n"); return IRQ_NONE; } reg_val = puma7_pmic_write(pmic, PUMA7_PMIC_PWRSRCIRQ_REG, PUMA7_PMIC_THRMIRQ_DCINDET); if (reg_val < 0) { dev_err(&pmic->client->dev, "PMIC PWRSRCIRQ reg write failed!\n"); return IRQ_NONE; } } return IRQ_HANDLED; } static int puma7_pmic_regulator_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct regulator_dev **rdev; struct device *dev = &client->dev; struct puma7_pmic_platform_data *pdata; struct puma7_pmic_subdev_data *sub; struct regulator_config config = { }; struct puma7_pmic *pmic; int i, ret = -EINVAL; unsigned long driver_private_data; struct gpio_desc *pmic_gpio; int pwr_src; int reg_val; driver_private_data = i2c_id->driver_data; pmic = devm_kzalloc(dev, sizeof(struct puma7_pmic) + sizeof(struct regulator_dev *) * PUMA7_PMIC_SUPPLY_NUMBER, GFP_KERNEL); if (!pmic) return -ENOMEM; pmic->client = client; pmic->handle = ACPI_HANDLE(dev); pmic->mode = PUMA7_PMIC_MODE_ACTIVE; rdev = pmic->rdev; pdata = &(pmic->platform_data); pdata->num_subdevs = PUMA7_PMIC_SUPPLY_NUMBER; pdata->subdevs = devm_kzalloc(dev, sizeof(struct puma7_pmic_subdev_data) * PUMA7_PMIC_SUPPLY_NUMBER, GFP_KERNEL); if (!pdata->subdevs) return -ENOMEM; sub = pdata->subdevs; for (i = 0; i < PUMA7_PMIC_SUPPLY_NUMBER; i++) { sub->id = puma7_pmic_reg[i].id; strlcpy(sub->name, puma7_pmic_reg[i].name, NAME_MAX_LEN); sub->regulator_data.regulator_init = puma7_pmic_regulator_init; sub->regulator_data.driver_data = sub; sub++; } /* Finally register regulators */ for (i = 0; i < pdata->num_subdevs; i++) { config.dev = dev; config.init_data = &(pdata->subdevs[i].regulator_data); config.driver_data = pmic; rdev[i] = regulator_register(&puma7_pmic_reg[i], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(&client->dev, "failed to register %s\n", puma7_pmic_reg[i].name); goto err_unregister; } } i2c_set_clientdata(client, pmic); ret = puma7_pmic_init_i2c_dummys(pmic); if (ret < 0) goto err_unregister; pmic_gpio = devm_gpiod_get_index(&client->dev, "PMIC", 0, GPIOD_IN); if (IS_ERR(pmic_gpio)) { dev_err(dev, "GPIO INT resource not found for PMIC device\n"); } else { client->irq = gpiod_to_irq(pmic_gpio); ret = devm_request_threaded_irq(dev, client->irq, NULL, puma7_pmic_isr, IRQF_TRIGGER_RISING | IRQF_ONESHOT, ACPI_PUMA7_PMIC_DEVICE_NAME, pmic); } if (avm_hw_info.hwrev == 233 || avm_hw_info.hwrev == 267) pmic_sysfs_genattrs(dev); if (ret == 0) { /* configure the PMIC */ if ((ret = puma7_pmic_write(pmic, PUMA7_PMIC_LPMCTRL_REG, PUMA7_PMIC_LPM_CFG_ACTIVE)) < 0) { dev_err(dev, "PMIC LPMCTRL reg write failed: 0x%x\n", PUMA7_PMIC_LPMCTRL_REG); } else if (((ret = puma7_pmic_read(pmic, PUMA7_PMIC_LPMCTRL_REG)) & PUMA7_PMIC_LPM_CFG_MASK) != PUMA7_PMIC_LPM_CFG_ACTIVE) { dev_err(dev, "PMIC LPMCTRL reg read failed: 0x%x\n", PUMA7_PMIC_LPMCTRL_REG); } else if ((ret = puma7_pmic_write(pmic, PUMA7_PMIC_MIRQLVL1_REG, ~(PUMA7_PMIC_IRQLVL1_THRM | PUMA7_PMIC_IRQLVL1_PWRSRC))) < 0) { dev_err(dev, "PMIC MIRQLVL1 reg write failed: 0x%x\n", PUMA7_PMIC_MIRQLVL1_REG); } else if ((ret = puma7_pmic_write(pmic, PUMA7_PMIC_MTHRMIRQ_REG, ~(PUMA7_PMIC_THRMIRQ_PMICHOT))) < 0) { dev_err(dev, "PMIC MTHRMIRQ reg write failed: 0x%x\n", PUMA7_PMIC_MTHRMIRQ_REG); } else if ((ret = puma7_pmic_write(pmic, PUMA7_PMIC_MPWRSRCIRQS0_REG, ~(PUMA7_PMIC_THRMIRQ_DCINDET))) < 0) { dev_err(dev, "PMIC MPWRSRCIRQS0 reg write failed: 0x%x\n", PUMA7_PMIC_MPWRSRCIRQS0_REG); } else if ((ret = puma7_pmic_write(pmic, PUMA7_PMIC_MPWRSRCIRQSX_REG, ~(PUMA7_PMIC_THRMIRQ_DCINDET))) < 0) { dev_err(dev, "PMIC MPWRSRCIRQSX reg write failed: 0x%x\n", PUMA7_PMIC_MPWRSRCIRQSX_REG); } else if ((reg_val = puma7_pmic_read(pmic, PUMA7_PMIC_SPWRSRC_REG)) < 0) { dev_err(dev, "PMIC SPWRSRC reg read error in regulator probe!\n"); } else { pwr_src = (reg_val & PUMA7_PMIC_SPWRSRC_SDCINDET) ? 1 : 0; // 0 = Disconnected 1 = Connected if ((puma7_pmic_ac_dc_indication(pmic, pwr_src)) < 0) { dev_err(dev, "PMIC ac dc indication error in regulator probe!\n"); } else { /* In some cases, where PMIC interrupt flag is set before PMIC initialization. * during that case, PMIC was not able to generate the BBU/AC mode switch event. * Fix is to reset the interrupt status register during PMIC initialization. */ reg_val = puma7_pmic_write(pmic, PUMA7_PMIC_PWRSRCIRQ_REG, PUMA7_PMIC_THRMIRQ_DCINDET); if (reg_val < 0) dev_err(&pmic->client->dev, "PMIC PWRSRCIRQ reg write error in regulator probe!\n"); else { if (avm_hw_info.hwrev == 233 || avm_hw_info.hwrev == 267) { if (puma7_pmic_regulator_config(pmic)) pr_err("[%s]: pmic regulator config failed.\n", __func__); else pr_info("[%s]: pmic regulator config succeed.\n", __func__); puma7_pmic_regulator_read_once(pmic); } return 0; } } } } err_unregister: for (i = 0; i < pdata->num_subdevs; i++) regulator_unregister(rdev[i]); return ret; } static int puma7_pmic_regulator_remove(struct i2c_client *client) { struct puma7_pmic *pmic = i2c_get_clientdata(client); int i; for (i = 0; i < PUMA7_PMIC_SUPPLY_NUMBER; i++) regulator_unregister(pmic->rdev[i]); if (avm_hw_info.hwrev == 233 || avm_hw_info.hwrev == 267) pmic_sysfs_remove_attrs(&client->dev); gpio_free(client->irq); return 0; } static const struct i2c_device_id puma7_pmic_id[] = { { .name = ACPI_PUMA7_PMIC_DEVICE_NAME, .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(i2c, puma7_pmic_id); static struct acpi_device_id puma7_pmic_acpi_match[] = { { ACPI_PUMA7_PMIC_DEVICE_NAME, 0}, { }, }; MODULE_DEVICE_TABLE(acpi, puma7_pmic_acpi_match); static struct gpiod_lookup_table puma7_pmic_gpios_table = { .dev_id = "i2c-INT34D9:00", .table = { GPIO_LOOKUP("INT33FF:01", 0x17, "PMIC", GPIO_ACTIVE_HIGH), { }, }, }; static struct i2c_driver puma7_pmic_driver = { .probe = puma7_pmic_regulator_probe, .remove = puma7_pmic_regulator_remove, .driver = { .name = ACPI_PUMA7_PMIC_DEVICE_NAME, .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(puma7_pmic_acpi_match), }, .id_table = puma7_pmic_id, }; static int __init puma7_pmic_init(void) { gpiod_add_lookup_table(&puma7_pmic_gpios_table); avm_hw_info = avm_get_hwrev(); return i2c_add_driver(&puma7_pmic_driver); } rootfs_initcall(puma7_pmic_init); static void __exit puma7_pmic_exit(void) { i2c_del_driver(&puma7_pmic_driver); } module_exit(puma7_pmic_exit); /* Module information */ MODULE_DESCRIPTION("PUMA7 PMIC voltage regulator driver"); MODULE_AUTHOR("Vinay Patel"); MODULE_LICENSE("GPL v2");