xref: /linux/drivers/regulator/bq257xx-regulator.c (revision 4f38da1f027ea2c9f01bb71daa7a299c191b6940)
1*981dd162SChris Morgan // SPDX-License-Identifier: GPL-2.0
2*981dd162SChris Morgan /*
3*981dd162SChris Morgan  * BQ257XX Battery Charger Driver
4*981dd162SChris Morgan  * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com>
5*981dd162SChris Morgan  */
6*981dd162SChris Morgan 
7*981dd162SChris Morgan #include <linux/bitfield.h>
8*981dd162SChris Morgan #include <linux/err.h>
9*981dd162SChris Morgan #include <linux/gpio/consumer.h>
10*981dd162SChris Morgan #include <linux/mfd/bq257xx.h>
11*981dd162SChris Morgan #include <linux/of.h>
12*981dd162SChris Morgan #include <linux/platform_device.h>
13*981dd162SChris Morgan #include <linux/regmap.h>
14*981dd162SChris Morgan #include <linux/regulator/driver.h>
15*981dd162SChris Morgan #include <linux/regulator/of_regulator.h>
16*981dd162SChris Morgan 
17*981dd162SChris Morgan struct bq257xx_reg_data {
18*981dd162SChris Morgan 	struct bq257xx_device *bq;
19*981dd162SChris Morgan 	struct regulator_dev *bq257xx_reg;
20*981dd162SChris Morgan 	struct gpio_desc *otg_en_gpio;
21*981dd162SChris Morgan 	struct regulator_desc desc;
22*981dd162SChris Morgan };
23*981dd162SChris Morgan 
24*981dd162SChris Morgan static int bq25703_vbus_get_cur_limit(struct regulator_dev *rdev)
25*981dd162SChris Morgan {
26*981dd162SChris Morgan 	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
27*981dd162SChris Morgan 	int ret;
28*981dd162SChris Morgan 	unsigned int reg;
29*981dd162SChris Morgan 
30*981dd162SChris Morgan 	ret = regmap_read(pdata->bq->regmap, BQ25703_OTG_CURRENT, &reg);
31*981dd162SChris Morgan 	if (ret)
32*981dd162SChris Morgan 		return ret;
33*981dd162SChris Morgan 	return FIELD_GET(BQ25703_OTG_CUR_MASK, reg) * BQ25703_OTG_CUR_STEP_UA;
34*981dd162SChris Morgan }
35*981dd162SChris Morgan 
36*981dd162SChris Morgan /*
37*981dd162SChris Morgan  * Check if the minimum current and maximum current requested are
38*981dd162SChris Morgan  * sane values, then set the register accordingly.
39*981dd162SChris Morgan  */
40*981dd162SChris Morgan static int bq25703_vbus_set_cur_limit(struct regulator_dev *rdev,
41*981dd162SChris Morgan 				      int min_uA, int max_uA)
42*981dd162SChris Morgan {
43*981dd162SChris Morgan 	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
44*981dd162SChris Morgan 	unsigned int reg;
45*981dd162SChris Morgan 
46*981dd162SChris Morgan 	if ((min_uA > BQ25703_OTG_CUR_MAX_UA) || (max_uA < 0))
47*981dd162SChris Morgan 		return -EINVAL;
48*981dd162SChris Morgan 
49*981dd162SChris Morgan 	reg = (max_uA / BQ25703_OTG_CUR_STEP_UA);
50*981dd162SChris Morgan 
51*981dd162SChris Morgan 	/* Catch rounding errors since our step is 50000uA. */
52*981dd162SChris Morgan 	if ((reg * BQ25703_OTG_CUR_STEP_UA) < min_uA)
53*981dd162SChris Morgan 		return -EINVAL;
54*981dd162SChris Morgan 
55*981dd162SChris Morgan 	return regmap_write(pdata->bq->regmap, BQ25703_OTG_CURRENT,
56*981dd162SChris Morgan 			    FIELD_PREP(BQ25703_OTG_CUR_MASK, reg));
57*981dd162SChris Morgan }
58*981dd162SChris Morgan 
59*981dd162SChris Morgan static int bq25703_vbus_enable(struct regulator_dev *rdev)
60*981dd162SChris Morgan {
61*981dd162SChris Morgan 	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
62*981dd162SChris Morgan 
63*981dd162SChris Morgan 	if (pdata->otg_en_gpio)
64*981dd162SChris Morgan 		gpiod_set_value_cansleep(pdata->otg_en_gpio, 1);
65*981dd162SChris Morgan 	return regulator_enable_regmap(rdev);
66*981dd162SChris Morgan }
67*981dd162SChris Morgan 
68*981dd162SChris Morgan static int bq25703_vbus_disable(struct regulator_dev *rdev)
69*981dd162SChris Morgan {
70*981dd162SChris Morgan 	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
71*981dd162SChris Morgan 
72*981dd162SChris Morgan 	if (pdata->otg_en_gpio)
73*981dd162SChris Morgan 		gpiod_set_value_cansleep(pdata->otg_en_gpio, 0);
74*981dd162SChris Morgan 	return regulator_disable_regmap(rdev);
75*981dd162SChris Morgan }
76*981dd162SChris Morgan 
77*981dd162SChris Morgan static const struct regulator_ops bq25703_vbus_ops = {
78*981dd162SChris Morgan 	.enable = bq25703_vbus_enable,
79*981dd162SChris Morgan 	.disable = bq25703_vbus_disable,
80*981dd162SChris Morgan 	.is_enabled = regulator_is_enabled_regmap,
81*981dd162SChris Morgan 	.list_voltage = regulator_list_voltage_linear,
82*981dd162SChris Morgan 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
83*981dd162SChris Morgan 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
84*981dd162SChris Morgan 	.get_current_limit = bq25703_vbus_get_cur_limit,
85*981dd162SChris Morgan 	.set_current_limit = bq25703_vbus_set_cur_limit,
86*981dd162SChris Morgan };
87*981dd162SChris Morgan 
88*981dd162SChris Morgan static const struct regulator_desc bq25703_vbus_desc = {
89*981dd162SChris Morgan 	.name = "vbus",
90*981dd162SChris Morgan 	.of_match = of_match_ptr("vbus"),
91*981dd162SChris Morgan 	.regulators_node = of_match_ptr("regulators"),
92*981dd162SChris Morgan 	.type = REGULATOR_VOLTAGE,
93*981dd162SChris Morgan 	.owner = THIS_MODULE,
94*981dd162SChris Morgan 	.ops = &bq25703_vbus_ops,
95*981dd162SChris Morgan 	.min_uV = BQ25703_OTG_VOLT_MIN_UV,
96*981dd162SChris Morgan 	.uV_step = BQ25703_OTG_VOLT_STEP_UV,
97*981dd162SChris Morgan 	.n_voltages = BQ25703_OTG_VOLT_NUM_VOLT,
98*981dd162SChris Morgan 	.enable_mask = BQ25703_EN_OTG_MASK,
99*981dd162SChris Morgan 	.enable_reg = BQ25703_CHARGE_OPTION_3,
100*981dd162SChris Morgan 	.enable_val = BQ25703_EN_OTG_MASK,
101*981dd162SChris Morgan 	.disable_val = 0,
102*981dd162SChris Morgan 	.vsel_reg = BQ25703_OTG_VOLT,
103*981dd162SChris Morgan 	.vsel_mask = BQ25703_OTG_VOLT_MASK,
104*981dd162SChris Morgan };
105*981dd162SChris Morgan 
106*981dd162SChris Morgan /* Get optional GPIO for OTG regulator enable. */
107*981dd162SChris Morgan static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev)
108*981dd162SChris Morgan {
109*981dd162SChris Morgan 	struct device_node *child, *subchild;
110*981dd162SChris Morgan 	struct bq257xx_reg_data *pdata = platform_get_drvdata(pdev);
111*981dd162SChris Morgan 
112*981dd162SChris Morgan 	child = of_get_child_by_name(pdev->dev.of_node,
113*981dd162SChris Morgan 				     pdata->desc.regulators_node);
114*981dd162SChris Morgan 	if (!child)
115*981dd162SChris Morgan 		return;
116*981dd162SChris Morgan 
117*981dd162SChris Morgan 	subchild = of_get_child_by_name(child, pdata->desc.of_match);
118*981dd162SChris Morgan 	if (!subchild)
119*981dd162SChris Morgan 		return;
120*981dd162SChris Morgan 
121*981dd162SChris Morgan 	of_node_put(child);
122*981dd162SChris Morgan 
123*981dd162SChris Morgan 	pdata->otg_en_gpio = devm_fwnode_gpiod_get_index(&pdev->dev,
124*981dd162SChris Morgan 							 of_fwnode_handle(subchild),
125*981dd162SChris Morgan 							 "enable", 0,
126*981dd162SChris Morgan 							 GPIOD_OUT_LOW,
127*981dd162SChris Morgan 							 pdata->desc.of_match);
128*981dd162SChris Morgan 
129*981dd162SChris Morgan 	of_node_put(subchild);
130*981dd162SChris Morgan 
131*981dd162SChris Morgan 	if (IS_ERR(pdata->otg_en_gpio)) {
132*981dd162SChris Morgan 		dev_err(&pdev->dev, "Error getting enable gpio: %ld\n",
133*981dd162SChris Morgan 			PTR_ERR(pdata->otg_en_gpio));
134*981dd162SChris Morgan 		return;
135*981dd162SChris Morgan 	}
136*981dd162SChris Morgan }
137*981dd162SChris Morgan 
138*981dd162SChris Morgan static int bq257xx_regulator_probe(struct platform_device *pdev)
139*981dd162SChris Morgan {
140*981dd162SChris Morgan 	struct device *dev = &pdev->dev;
141*981dd162SChris Morgan 	struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);
142*981dd162SChris Morgan 	struct bq257xx_reg_data *pdata;
143*981dd162SChris Morgan 	struct device_node *np = dev->of_node;
144*981dd162SChris Morgan 	struct regulator_config cfg = {};
145*981dd162SChris Morgan 
146*981dd162SChris Morgan 	pdev->dev.of_node = pdev->dev.parent->of_node;
147*981dd162SChris Morgan 	pdev->dev.of_node_reused = true;
148*981dd162SChris Morgan 
149*981dd162SChris Morgan 	pdata = devm_kzalloc(&pdev->dev, sizeof(struct bq257xx_reg_data), GFP_KERNEL);
150*981dd162SChris Morgan 	if (!pdata)
151*981dd162SChris Morgan 		return -ENOMEM;
152*981dd162SChris Morgan 
153*981dd162SChris Morgan 	pdata->bq = bq;
154*981dd162SChris Morgan 	pdata->desc = bq25703_vbus_desc;
155*981dd162SChris Morgan 
156*981dd162SChris Morgan 	platform_set_drvdata(pdev, pdata);
157*981dd162SChris Morgan 	bq257xx_reg_dt_parse_gpio(pdev);
158*981dd162SChris Morgan 
159*981dd162SChris Morgan 	cfg.dev = &pdev->dev;
160*981dd162SChris Morgan 	cfg.driver_data = pdata;
161*981dd162SChris Morgan 	cfg.of_node = np;
162*981dd162SChris Morgan 	cfg.regmap = dev_get_regmap(pdev->dev.parent, NULL);
163*981dd162SChris Morgan 	if (!cfg.regmap)
164*981dd162SChris Morgan 		return -ENODEV;
165*981dd162SChris Morgan 
166*981dd162SChris Morgan 	pdata->bq257xx_reg = devm_regulator_register(dev, &pdata->desc, &cfg);
167*981dd162SChris Morgan 	if (IS_ERR(pdata->bq257xx_reg)) {
168*981dd162SChris Morgan 		return dev_err_probe(&pdev->dev, PTR_ERR(pdata->bq257xx_reg),
169*981dd162SChris Morgan 				     "error registering bq257xx regulator");
170*981dd162SChris Morgan 	}
171*981dd162SChris Morgan 
172*981dd162SChris Morgan 	return 0;
173*981dd162SChris Morgan }
174*981dd162SChris Morgan 
175*981dd162SChris Morgan static struct platform_driver bq257xx_reg_driver = {
176*981dd162SChris Morgan 	.driver = {
177*981dd162SChris Morgan 		.name = "bq257xx-regulator",
178*981dd162SChris Morgan 	},
179*981dd162SChris Morgan 	.probe = bq257xx_regulator_probe,
180*981dd162SChris Morgan };
181*981dd162SChris Morgan 
182*981dd162SChris Morgan module_platform_driver(bq257xx_reg_driver);
183*981dd162SChris Morgan 
184*981dd162SChris Morgan MODULE_DESCRIPTION("bq257xx regulator driver");
185*981dd162SChris Morgan MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
186*981dd162SChris Morgan MODULE_LICENSE("GPL");
187