188 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2021 ROHM Semiconductors4 *5 * ROHM BD9576MUF and BD9573MUF PMIC driver6 */7 8#include <linux/i2c.h>9#include <linux/interrupt.h>10#include <linux/ioport.h>11#include <linux/irq.h>12#include <linux/mfd/core.h>13#include <linux/mfd/rohm-bd957x.h>14#include <linux/mfd/rohm-generic.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/regmap.h>18#include <linux/types.h>19 20enum {21 BD957X_REGULATOR_CELL,22 BD957X_WDT_CELL,23};24 25/*26 * Due to the BD9576MUF nasty IRQ behaviour we don't always populate IRQs.27 * These will be added to regulator resources only if IRQ information for the28 * PMIC is populated in device-tree.29 */30static const struct resource bd9576_regulator_irqs[] = {31 DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp"),32 DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd"),33 DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd"),34};35 36static struct mfd_cell bd9573_mfd_cells[] = {37 [BD957X_REGULATOR_CELL] = { .name = "bd9573-regulator", },38 [BD957X_WDT_CELL] = { .name = "bd9576-wdt", },39};40 41static struct mfd_cell bd9576_mfd_cells[] = {42 [BD957X_REGULATOR_CELL] = { .name = "bd9576-regulator", },43 [BD957X_WDT_CELL] = { .name = "bd9576-wdt", },44};45 46static const struct regmap_range volatile_ranges[] = {47 regmap_reg_range(BD957X_REG_SMRB_ASSERT, BD957X_REG_SMRB_ASSERT),48 regmap_reg_range(BD957X_REG_PMIC_INTERNAL_STAT,49 BD957X_REG_PMIC_INTERNAL_STAT),50 regmap_reg_range(BD957X_REG_INT_THERM_STAT, BD957X_REG_INT_THERM_STAT),51 regmap_reg_range(BD957X_REG_INT_OVP_STAT, BD957X_REG_INT_SYS_STAT),52 regmap_reg_range(BD957X_REG_INT_MAIN_STAT, BD957X_REG_INT_MAIN_STAT),53};54 55static const struct regmap_access_table volatile_regs = {56 .yes_ranges = &volatile_ranges[0],57 .n_yes_ranges = ARRAY_SIZE(volatile_ranges),58};59 60static const struct regmap_config bd957x_regmap = {61 .reg_bits = 8,62 .val_bits = 8,63 .volatile_table = &volatile_regs,64 .max_register = BD957X_MAX_REGISTER,65 .cache_type = REGCACHE_MAPLE,66};67 68static const struct regmap_irq bd9576_irqs[] = {69 REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM),70 REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP),71 REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP),72 REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP),73 REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD),74 REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD),75 REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP),76 REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS),77};78 79static const struct regmap_irq_chip bd9576_irq_chip = {80 .name = "bd9576_irq",81 .irqs = &bd9576_irqs[0],82 .num_irqs = ARRAY_SIZE(bd9576_irqs),83 .status_base = BD957X_REG_INT_MAIN_STAT,84 .mask_base = BD957X_REG_INT_MAIN_MASK,85 .ack_base = BD957X_REG_INT_MAIN_STAT,86 .init_ack_masked = true,87 .num_regs = 1,88 .irq_reg_stride = 1,89};90 91static int bd957x_i2c_probe(struct i2c_client *i2c)92{93 int ret;94 struct regmap *regmap;95 struct mfd_cell *cells;96 int num_cells;97 unsigned long chip_type;98 struct irq_domain *domain;99 bool usable_irqs;100 101 chip_type = (unsigned long)of_device_get_match_data(&i2c->dev);102 103 switch (chip_type) {104 case ROHM_CHIP_TYPE_BD9576:105 cells = bd9576_mfd_cells;106 num_cells = ARRAY_SIZE(bd9576_mfd_cells);107 usable_irqs = !!i2c->irq;108 break;109 case ROHM_CHIP_TYPE_BD9573:110 cells = bd9573_mfd_cells;111 num_cells = ARRAY_SIZE(bd9573_mfd_cells);112 /*113 * BD9573 only supports fatal IRQs which we can not handle114 * because SoC is going to lose the power.115 */116 usable_irqs = false;117 break;118 default:119 dev_err(&i2c->dev, "Unknown device type");120 return -EINVAL;121 }122 123 regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap);124 if (IS_ERR(regmap))125 return dev_err_probe(&i2c->dev, PTR_ERR(regmap),126 "Failed to initialize Regmap\n");127 128 /*129 * BD9576 behaves badly. It kepts IRQ line asserted for the whole130 * duration of detected HW condition (like over temperature). So we131 * don't require IRQ to be populated.132 * If IRQ information is not given, then we mask all IRQs and do not133 * provide IRQ resources to regulator driver - which then just omits134 * the notifiers.135 */136 if (usable_irqs) {137 struct regmap_irq_chip_data *irq_data;138 struct mfd_cell *regulators;139 140 regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL];141 regulators->resources = bd9576_regulator_irqs;142 regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs);143 144 ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,145 IRQF_ONESHOT, 0,146 &bd9576_irq_chip, &irq_data);147 if (ret)148 return dev_err_probe(&i2c->dev, ret,149 "Failed to add IRQ chip\n");150 151 domain = regmap_irq_get_domain(irq_data);152 } else {153 ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK,154 BD957X_MASK_INT_ALL,155 BD957X_MASK_INT_ALL);156 if (ret)157 return ret;158 domain = NULL;159 }160 161 ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells,162 num_cells, NULL, 0, domain);163 if (ret)164 dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n");165 166 return ret;167}168 169static const struct of_device_id bd957x_of_match[] = {170 { .compatible = "rohm,bd9576", .data = (void *)ROHM_CHIP_TYPE_BD9576, },171 { .compatible = "rohm,bd9573", .data = (void *)ROHM_CHIP_TYPE_BD9573, },172 { },173};174MODULE_DEVICE_TABLE(of, bd957x_of_match);175 176static struct i2c_driver bd957x_drv = {177 .driver = {178 .name = "rohm-bd957x",179 .of_match_table = bd957x_of_match,180 },181 .probe = bd957x_i2c_probe,182};183module_i2c_driver(bd957x_drv);184 185MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");186MODULE_DESCRIPTION("ROHM BD9576MUF and BD9573MUF Power Management IC driver");187MODULE_LICENSE("GPL");188