226 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// da9210-regulator.c - Regulator device driver for DA92104// Copyright (C) 2013 Dialog Semiconductor Ltd.5 6#include <linux/err.h>7#include <linux/i2c.h>8#include <linux/module.h>9#include <linux/interrupt.h>10#include <linux/irq.h>11#include <linux/regulator/driver.h>12#include <linux/regulator/machine.h>13#include <linux/of.h>14#include <linux/regulator/of_regulator.h>15#include <linux/regmap.h>16 17#include "da9210-regulator.h"18 19struct da9210 {20 struct regulator_dev *rdev;21 struct regmap *regmap;22};23 24static const struct regmap_config da9210_regmap_config = {25 .reg_bits = 8,26 .val_bits = 8,27};28 29static const struct regulator_ops da9210_buck_ops = {30 .enable = regulator_enable_regmap,31 .disable = regulator_disable_regmap,32 .is_enabled = regulator_is_enabled_regmap,33 .set_voltage_sel = regulator_set_voltage_sel_regmap,34 .get_voltage_sel = regulator_get_voltage_sel_regmap,35 .list_voltage = regulator_list_voltage_linear,36 .set_current_limit = regulator_set_current_limit_regmap,37 .get_current_limit = regulator_get_current_limit_regmap,38};39 40/* Default limits measured in millivolts and milliamps */41#define DA9210_MIN_MV 30042#define DA9210_MAX_MV 157043#define DA9210_STEP_MV 1044 45/* Current limits for buck (uA) indices corresponds with register values */46static const unsigned int da9210_buck_limits[] = {47 1600000, 1800000, 2000000, 2200000, 2400000, 2600000, 2800000, 3000000,48 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, 460000049};50 51static const struct regulator_desc da9210_reg = {52 .name = "DA9210",53 .id = 0,54 .ops = &da9210_buck_ops,55 .type = REGULATOR_VOLTAGE,56 .n_voltages = ((DA9210_MAX_MV - DA9210_MIN_MV) / DA9210_STEP_MV) + 1,57 .min_uV = (DA9210_MIN_MV * 1000),58 .uV_step = (DA9210_STEP_MV * 1000),59 .vsel_reg = DA9210_REG_VBUCK_A,60 .vsel_mask = DA9210_VBUCK_MASK,61 .enable_reg = DA9210_REG_BUCK_CONT,62 .enable_mask = DA9210_BUCK_EN,63 .owner = THIS_MODULE,64 .curr_table = da9210_buck_limits,65 .n_current_limits = ARRAY_SIZE(da9210_buck_limits),66 .csel_reg = DA9210_REG_BUCK_ILIM,67 .csel_mask = DA9210_BUCK_ILIM_MASK,68};69 70static irqreturn_t da9210_irq_handler(int irq, void *data)71{72 struct da9210 *chip = data;73 unsigned int val, handled = 0;74 int error, ret = IRQ_NONE;75 76 error = regmap_read(chip->regmap, DA9210_REG_EVENT_B, &val);77 if (error < 0)78 goto error_i2c;79 80 if (val & DA9210_E_OVCURR) {81 regulator_notifier_call_chain(chip->rdev,82 REGULATOR_EVENT_OVER_CURRENT,83 NULL);84 handled |= DA9210_E_OVCURR;85 }86 if (val & DA9210_E_NPWRGOOD) {87 regulator_notifier_call_chain(chip->rdev,88 REGULATOR_EVENT_UNDER_VOLTAGE,89 NULL);90 handled |= DA9210_E_NPWRGOOD;91 }92 if (val & (DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT)) {93 regulator_notifier_call_chain(chip->rdev,94 REGULATOR_EVENT_OVER_TEMP, NULL);95 handled |= val & (DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT);96 }97 if (val & DA9210_E_VMAX) {98 regulator_notifier_call_chain(chip->rdev,99 REGULATOR_EVENT_REGULATION_OUT,100 NULL);101 handled |= DA9210_E_VMAX;102 }103 104 if (handled) {105 /* Clear handled events */106 error = regmap_write(chip->regmap, DA9210_REG_EVENT_B, handled);107 if (error < 0)108 goto error_i2c;109 110 ret = IRQ_HANDLED;111 }112 113 return ret;114 115error_i2c:116 dev_err(regmap_get_device(chip->regmap), "I2C error : %d\n", error);117 return ret;118}119 120/*121 * I2C driver interface functions122 */123 124static const struct of_device_id __maybe_unused da9210_dt_ids[] = {125 { .compatible = "dlg,da9210", },126 { }127};128MODULE_DEVICE_TABLE(of, da9210_dt_ids);129 130static int da9210_i2c_probe(struct i2c_client *i2c)131{132 struct da9210 *chip;133 struct device *dev = &i2c->dev;134 struct da9210_pdata *pdata = dev_get_platdata(dev);135 struct regulator_dev *rdev = NULL;136 struct regulator_config config = { };137 int error;138 139 chip = devm_kzalloc(&i2c->dev, sizeof(struct da9210), GFP_KERNEL);140 if (!chip)141 return -ENOMEM;142 143 chip->regmap = devm_regmap_init_i2c(i2c, &da9210_regmap_config);144 if (IS_ERR(chip->regmap)) {145 error = PTR_ERR(chip->regmap);146 dev_err(&i2c->dev, "Failed to allocate register map: %d\n",147 error);148 return error;149 }150 151 config.dev = &i2c->dev;152 config.init_data = pdata ? &pdata->da9210_constraints :153 of_get_regulator_init_data(dev, dev->of_node, &da9210_reg);154 config.driver_data = chip;155 config.regmap = chip->regmap;156 config.of_node = dev->of_node;157 158 /* Mask all interrupt sources to deassert interrupt line */159 error = regmap_write(chip->regmap, DA9210_REG_MASK_A, ~0);160 if (!error)161 error = regmap_write(chip->regmap, DA9210_REG_MASK_B, ~0);162 if (error) {163 dev_err(&i2c->dev, "Failed to write to mask reg: %d\n", error);164 return error;165 }166 167 rdev = devm_regulator_register(&i2c->dev, &da9210_reg, &config);168 if (IS_ERR(rdev)) {169 dev_err(&i2c->dev, "Failed to register DA9210 regulator\n");170 return PTR_ERR(rdev);171 }172 173 chip->rdev = rdev;174 if (i2c->irq) {175 error = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,176 da9210_irq_handler,177 IRQF_TRIGGER_LOW |178 IRQF_ONESHOT | IRQF_SHARED,179 "da9210", chip);180 if (error) {181 dev_err(&i2c->dev, "Failed to request IRQ%u: %d\n",182 i2c->irq, error);183 return error;184 }185 186 error = regmap_update_bits(chip->regmap, DA9210_REG_MASK_B,187 DA9210_M_OVCURR | DA9210_M_NPWRGOOD |188 DA9210_M_TEMP_WARN |189 DA9210_M_TEMP_CRIT | DA9210_M_VMAX, 0);190 if (error < 0) {191 dev_err(&i2c->dev, "Failed to update mask reg: %d\n",192 error);193 return error;194 }195 } else {196 dev_warn(&i2c->dev, "No IRQ configured\n");197 }198 199 i2c_set_clientdata(i2c, chip);200 201 return 0;202}203 204static const struct i2c_device_id da9210_i2c_id[] = {205 { "da9210" },206 {}207};208 209MODULE_DEVICE_TABLE(i2c, da9210_i2c_id);210 211static struct i2c_driver da9210_regulator_driver = {212 .driver = {213 .name = "da9210",214 .probe_type = PROBE_PREFER_ASYNCHRONOUS,215 .of_match_table = of_match_ptr(da9210_dt_ids),216 },217 .probe = da9210_i2c_probe,218 .id_table = da9210_i2c_id,219};220 221module_i2c_driver(da9210_regulator_driver);222 223MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");224MODULE_DESCRIPTION("Regulator device driver for Dialog DA9210");225MODULE_LICENSE("GPL v2");226