123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- // SPDX-License-Identifier: GPL-2.0+
- #include <linux/bits.h>
- #include <linux/gpio/consumer.h>
- #include <linux/interrupt.h>
- #include <linux/kernel.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- enum {
- MT6370_IDX_DSVBOOST = 0,
- MT6370_IDX_DSVPOS,
- MT6370_IDX_DSVNEG,
- MT6370_IDX_VIBLDO,
- MT6370_MAX_IDX
- };
- #define MT6370_REG_LDO_CFG 0x180
- #define MT6370_REG_LDO_VOUT 0x181
- #define MT6370_REG_DB_CTRL1 0x1B0
- #define MT6370_REG_DB_CTRL2 0x1B1
- #define MT6370_REG_DB_VBST 0x1B2
- #define MT6370_REG_DB_VPOS 0x1B3
- #define MT6370_REG_DB_VNEG 0x1B4
- #define MT6370_REG_LDO_STAT 0x1DC
- #define MT6370_REG_DB_STAT 0x1DF
- #define MT6370_LDOOMS_MASK BIT(7)
- #define MT6370_LDOEN_MASK BIT(7)
- #define MT6370_LDOVOUT_MASK GENMASK(3, 0)
- #define MT6370_DBPERD_MASK (BIT(7) | BIT(4))
- #define MT6370_DBEXTEN_MASK BIT(0)
- #define MT6370_DBVPOSEN_MASK BIT(6)
- #define MT6370_DBVPOSDISG_MASK BIT(5)
- #define MT6370_DBVNEGEN_MASK BIT(3)
- #define MT6370_DBVNEGDISG_MASK BIT(2)
- #define MT6370_DBALLON_MASK (MT6370_DBVPOSEN_MASK | MT6370_DBVNEGEN_MASK)
- #define MT6370_DBSLEW_MASK GENMASK(7, 6)
- #define MT6370_DBVOUT_MASK GENMASK(5, 0)
- #define MT6370_LDOOC_EVT_MASK BIT(7)
- #define MT6370_POSSCP_EVT_MASK BIT(7)
- #define MT6370_NEGSCP_EVT_MASK BIT(6)
- #define MT6370_BSTOCP_EVT_MASK BIT(5)
- #define MT6370_POSOCP_EVT_MASK BIT(4)
- #define MT6370_NEGOCP_EVT_MASK BIT(3)
- #define MT6370_LDO_MINUV 1600000
- #define MT6370_LDO_STPUV 200000
- #define MT6370_LDO_N_VOLT 13
- #define MT6370_DBVBOOST_MINUV 4000000
- #define MT6370_DBVBOOST_STPUV 50000
- #define MT6370_DBVBOOST_N_VOLT 45
- #define MT6370_DBVOUT_MINUV 4000000
- #define MT6370_DBVOUT_STPUV 50000
- #define MT6370_DBVOUT_N_VOLT 41
- struct mt6370_priv {
- struct device *dev;
- struct regmap *regmap;
- struct regulator_dev *rdev[MT6370_MAX_IDX];
- bool use_external_ctrl;
- };
- static const unsigned int mt6370_vpos_ramp_tbl[] = { 8540, 5840, 4830, 3000 };
- static const unsigned int mt6370_vneg_ramp_tbl[] = { 10090, 6310, 5050, 3150 };
- static int mt6370_get_error_flags(struct regulator_dev *rdev,
- unsigned int *flags)
- {
- struct regmap *regmap = rdev_get_regmap(rdev);
- unsigned int stat_reg, stat, rpt_flags = 0;
- int rid = rdev_get_id(rdev), ret;
- if (rid == MT6370_IDX_VIBLDO)
- stat_reg = MT6370_REG_LDO_STAT;
- else
- stat_reg = MT6370_REG_DB_STAT;
- ret = regmap_read(regmap, stat_reg, &stat);
- if (ret)
- return ret;
- switch (rid) {
- case MT6370_IDX_DSVBOOST:
- if (stat & MT6370_BSTOCP_EVT_MASK)
- rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
- break;
- case MT6370_IDX_DSVPOS:
- if (stat & MT6370_POSSCP_EVT_MASK)
- rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
- if (stat & MT6370_POSOCP_EVT_MASK)
- rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
- break;
- case MT6370_IDX_DSVNEG:
- if (stat & MT6370_NEGSCP_EVT_MASK)
- rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
- if (stat & MT6370_NEGOCP_EVT_MASK)
- rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
- break;
- default:
- if (stat & MT6370_LDOOC_EVT_MASK)
- rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
- break;
- }
- *flags = rpt_flags;
- return 0;
- }
- static const struct regulator_ops mt6370_dbvboost_ops = {
- .get_voltage_sel = regulator_get_voltage_sel_regmap,
- .set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = regulator_list_voltage_linear,
- .get_bypass = regulator_get_bypass_regmap,
- .set_bypass = regulator_set_bypass_regmap,
- .get_error_flags = mt6370_get_error_flags,
- };
- static const struct regulator_ops mt6370_dbvout_ops = {
- .get_voltage_sel = regulator_get_voltage_sel_regmap,
- .set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = regulator_list_voltage_linear,
- .is_enabled = regulator_is_enabled_regmap,
- .enable = regulator_enable_regmap,
- .disable = regulator_disable_regmap,
- .set_active_discharge = regulator_set_active_discharge_regmap,
- .set_ramp_delay = regulator_set_ramp_delay_regmap,
- .get_error_flags = mt6370_get_error_flags,
- };
- static const struct regulator_ops mt6370_ldo_ops = {
- .get_voltage_sel = regulator_get_voltage_sel_regmap,
- .set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = regulator_list_voltage_linear,
- .is_enabled = regulator_is_enabled_regmap,
- .enable = regulator_enable_regmap,
- .disable = regulator_disable_regmap,
- .set_active_discharge = regulator_set_active_discharge_regmap,
- .get_error_flags = mt6370_get_error_flags,
- };
- static int mt6370_of_parse_cb(struct device_node *np,
- const struct regulator_desc *desc,
- struct regulator_config *config)
- {
- struct mt6370_priv *priv = config->driver_data;
- struct gpio_desc *enable_gpio;
- int ret;
- enable_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0,
- GPIOD_OUT_HIGH |
- GPIOD_FLAGS_BIT_NONEXCLUSIVE,
- desc->name);
- if (IS_ERR(enable_gpio)) {
- config->ena_gpiod = NULL;
- return 0;
- }
- /*
- * RG control by default
- * Only if all are using external pin, change all by external control
- */
- if (priv->use_external_ctrl) {
- ret = regmap_update_bits(priv->regmap, MT6370_REG_DB_CTRL1,
- MT6370_DBEXTEN_MASK,
- MT6370_DBEXTEN_MASK);
- if (ret)
- return ret;
- }
- config->ena_gpiod = enable_gpio;
- priv->use_external_ctrl = true;
- return 0;
- }
- static const struct regulator_desc mt6370_regulator_descs[] = {
- {
- .name = "mt6370-dsv-vbst",
- .of_match = of_match_ptr("dsvbst"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MT6370_IDX_DSVBOOST,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .ops = &mt6370_dbvboost_ops,
- .min_uV = MT6370_DBVBOOST_MINUV,
- .uV_step = MT6370_DBVBOOST_STPUV,
- .n_voltages = MT6370_DBVBOOST_N_VOLT,
- .vsel_reg = MT6370_REG_DB_VBST,
- .vsel_mask = MT6370_DBVOUT_MASK,
- .bypass_reg = MT6370_REG_DB_CTRL1,
- .bypass_mask = MT6370_DBPERD_MASK,
- .bypass_val_on = MT6370_DBPERD_MASK,
- },
- {
- .name = "mt6370-dsv-vpos",
- .of_match = of_match_ptr("dsvpos"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MT6370_IDX_DSVPOS,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .of_parse_cb = mt6370_of_parse_cb,
- .ops = &mt6370_dbvout_ops,
- .min_uV = MT6370_DBVOUT_MINUV,
- .uV_step = MT6370_DBVOUT_STPUV,
- .n_voltages = MT6370_DBVOUT_N_VOLT,
- .vsel_reg = MT6370_REG_DB_VPOS,
- .vsel_mask = MT6370_DBVOUT_MASK,
- .enable_reg = MT6370_REG_DB_CTRL2,
- .enable_mask = MT6370_DBVPOSEN_MASK,
- .ramp_reg = MT6370_REG_DB_VPOS,
- .ramp_mask = MT6370_DBSLEW_MASK,
- .ramp_delay_table = mt6370_vpos_ramp_tbl,
- .n_ramp_values = ARRAY_SIZE(mt6370_vpos_ramp_tbl),
- .active_discharge_reg = MT6370_REG_DB_CTRL2,
- .active_discharge_mask = MT6370_DBVPOSDISG_MASK,
- .active_discharge_on = MT6370_DBVPOSDISG_MASK,
- },
- {
- .name = "mt6370-dsv-vneg",
- .of_match = of_match_ptr("dsvneg"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MT6370_IDX_DSVNEG,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .of_parse_cb = mt6370_of_parse_cb,
- .ops = &mt6370_dbvout_ops,
- .min_uV = MT6370_DBVOUT_MINUV,
- .uV_step = MT6370_DBVOUT_STPUV,
- .n_voltages = MT6370_DBVOUT_N_VOLT,
- .vsel_reg = MT6370_REG_DB_VNEG,
- .vsel_mask = MT6370_DBVOUT_MASK,
- .enable_reg = MT6370_REG_DB_CTRL2,
- .enable_mask = MT6370_DBVNEGEN_MASK,
- .ramp_reg = MT6370_REG_DB_VNEG,
- .ramp_mask = MT6370_DBSLEW_MASK,
- .ramp_delay_table = mt6370_vneg_ramp_tbl,
- .n_ramp_values = ARRAY_SIZE(mt6370_vneg_ramp_tbl),
- .active_discharge_reg = MT6370_REG_DB_CTRL2,
- .active_discharge_mask = MT6370_DBVNEGDISG_MASK,
- .active_discharge_on = MT6370_DBVNEGDISG_MASK,
- },
- {
- .name = "mt6370-vib-ldo",
- .of_match = of_match_ptr("vibldo"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MT6370_IDX_VIBLDO,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .ops = &mt6370_ldo_ops,
- .min_uV = MT6370_LDO_MINUV,
- .uV_step = MT6370_LDO_STPUV,
- .n_voltages = MT6370_LDO_N_VOLT,
- .vsel_reg = MT6370_REG_LDO_VOUT,
- .vsel_mask = MT6370_LDOVOUT_MASK,
- .enable_reg = MT6370_REG_LDO_VOUT,
- .enable_mask = MT6370_LDOEN_MASK,
- .active_discharge_reg = MT6370_REG_LDO_CFG,
- .active_discharge_mask = MT6370_LDOOMS_MASK,
- .active_discharge_on = MT6370_LDOOMS_MASK,
- }
- };
- static irqreturn_t mt6370_scp_handler(int irq, void *data)
- {
- struct regulator_dev *rdev = data;
- regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE,
- NULL);
- return IRQ_HANDLED;
- }
- static irqreturn_t mt6370_ocp_handler(int irq, void *data)
- {
- struct regulator_dev *rdev = data;
- regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL);
- return IRQ_HANDLED;
- }
- static int mt6370_regulator_irq_register(struct mt6370_priv *priv)
- {
- struct platform_device *pdev = to_platform_device(priv->dev);
- static const struct {
- const char *name;
- int rid;
- irq_handler_t handler;
- } mt6370_irqs[] = {
- { "db_vpos_scp", MT6370_IDX_DSVPOS, mt6370_scp_handler },
- { "db_vneg_scp", MT6370_IDX_DSVNEG, mt6370_scp_handler },
- { "db_vbst_ocp", MT6370_IDX_DSVBOOST, mt6370_ocp_handler },
- { "db_vpos_ocp", MT6370_IDX_DSVPOS, mt6370_ocp_handler },
- { "db_vneg_ocp", MT6370_IDX_DSVNEG, mt6370_ocp_handler },
- { "ldo_oc", MT6370_IDX_VIBLDO, mt6370_ocp_handler }
- };
- struct regulator_dev *rdev;
- int i, irq, ret;
- for (i = 0; i < ARRAY_SIZE(mt6370_irqs); i++) {
- irq = platform_get_irq_byname(pdev, mt6370_irqs[i].name);
- rdev = priv->rdev[mt6370_irqs[i].rid];
- ret = devm_request_threaded_irq(priv->dev, irq, NULL,
- mt6370_irqs[i].handler, 0,
- mt6370_irqs[i].name, rdev);
- if (ret) {
- dev_err(priv->dev,
- "Failed to register (%d) interrupt\n", i);
- return ret;
- }
- }
- return 0;
- }
- static int mt6370_regualtor_register(struct mt6370_priv *priv)
- {
- struct regulator_dev *rdev;
- struct regulator_config cfg = {};
- struct device *parent = priv->dev->parent;
- int i;
- cfg.dev = parent;
- cfg.driver_data = priv;
- for (i = 0; i < MT6370_MAX_IDX; i++) {
- rdev = devm_regulator_register(priv->dev,
- mt6370_regulator_descs + i,
- &cfg);
- if (IS_ERR(rdev)) {
- dev_err(priv->dev,
- "Failed to register (%d) regulator\n", i);
- return PTR_ERR(rdev);
- }
- priv->rdev[i] = rdev;
- }
- return 0;
- }
- static int mt6370_regulator_probe(struct platform_device *pdev)
- {
- struct mt6370_priv *priv;
- int ret;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->dev = &pdev->dev;
- priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!priv->regmap) {
- dev_err(&pdev->dev, "Failed to init regmap\n");
- return -ENODEV;
- }
- ret = mt6370_regualtor_register(priv);
- if (ret)
- return ret;
- return mt6370_regulator_irq_register(priv);
- }
- static const struct platform_device_id mt6370_devid_table[] = {
- { "mt6370-regulator", 0},
- {}
- };
- MODULE_DEVICE_TABLE(platform, mt6370_devid_table);
- static struct platform_driver mt6370_regulator_driver = {
- .driver = {
- .name = "mt6370-regulator",
- },
- .id_table = mt6370_devid_table,
- .probe = mt6370_regulator_probe,
- };
- module_platform_driver(mt6370_regulator_driver);
- MODULE_AUTHOR("ChiYuan Huang <[email protected]>");
- MODULE_DESCRIPTION("Mediatek MT6370 Regulator Driver");
- MODULE_LICENSE("GPL v2");
|