Skip to content

Commit 31eedc1

Browse files
committed
regulator: aw37503: add regulator driver for Awinic
Merge series from like@awinic.com: Add regulator driver for the device Awinic AW37503 which is single inductor - dual output power supply device. AW37503 device is designed to support general positive/negative driven applications like TFT display panels.
2 parents 3a5e6e4 + 86a1b61 commit 31eedc1

File tree

4 files changed

+327
-0
lines changed

4 files changed

+327
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/regulator/awinic,aw37503.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: Awinic AW37503 Voltage Regulator
8+
9+
maintainers:
10+
- Alec Li <like@awinic.com>
11+
12+
description:
13+
The AW37503 are dual voltage regulator, designed to support positive/negative
14+
supply for driving TFT-LCD panels. It support software-configurable output
15+
switching and monitoring. The output voltages can be programmed via an I2C
16+
compatible interface.
17+
18+
properties:
19+
compatible:
20+
const: awinic,aw37503
21+
22+
reg:
23+
maxItems: 1
24+
25+
patternProperties:
26+
"^out[pn]$":
27+
type: object
28+
$ref: regulator.yaml#
29+
unevaluatedProperties: false
30+
description:
31+
Properties for single regulator.
32+
33+
properties:
34+
enable-gpios:
35+
maxItems: 1
36+
description:
37+
GPIO specifier to enable the GPIO control (on/off) for regulator.
38+
39+
required:
40+
- regulator-name
41+
42+
required:
43+
- compatible
44+
- reg
45+
- outp
46+
- outn
47+
48+
additionalProperties: false
49+
50+
examples:
51+
- |
52+
#include <dt-bindings/gpio/gpio.h>
53+
54+
i2c {
55+
#address-cells = <1>;
56+
#size-cells = <0>;
57+
58+
regulator@3e {
59+
compatible = "awinic,aw37503";
60+
reg = <0x3e>;
61+
62+
outp {
63+
regulator-name = "outp";
64+
regulator-boot-on;
65+
regulator-always-on;
66+
enable-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
67+
};
68+
69+
outn {
70+
regulator-name = "outn";
71+
regulator-boot-on;
72+
regulator-always-on;
73+
enable-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
74+
};
75+
};
76+
};
77+
...
78+

drivers/regulator/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ config REGULATOR_ATC260X
178178
ATC260x PMICs. This will enable support for all the software
179179
controllable DCDC/LDO regulators.
180180

181+
config REGULATOR_AW37503
182+
tristate "Awinic AW37503 Dual Output Power regulators"
183+
depends on I2C && GPIOLIB
184+
select REGMAP_I2C
185+
help
186+
This driver supports AW37503 single inductor - dual output
187+
power supply specifically designed for display panels.
188+
181189
config REGULATOR_AXP20X
182190
tristate "X-POWERS AXP20X PMIC Regulators"
183191
depends on MFD_AXP20X

drivers/regulator/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o
2727
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
2828
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
2929
obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o
30+
obj-$(CONFIG_REGULATOR_AW37503) += aw37503-regulator.o
3031
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
3132
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
3233
obj-$(CONFIG_REGULATOR_BD71815) += bd71815-regulator.o

drivers/regulator/aw37503-regulator.c

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
//
3+
// AWINIC AW37503 Regulator Driver
4+
//
5+
// Copyright (C) 2023 awinic. All Rights Reserved
6+
//
7+
// Author: <like@awinic.com>
8+
9+
#include <linux/err.h>
10+
#include <linux/gpio/consumer.h>
11+
#include <linux/i2c.h>
12+
#include <linux/module.h>
13+
#include <linux/regmap.h>
14+
#include <linux/regulator/driver.h>
15+
#include <linux/regulator/machine.h>
16+
17+
#define AW37503_REG_VPOS 0x00
18+
#define AW37503_REG_VNEG 0x01
19+
#define AW37503_REG_APPS 0x03
20+
#define AW37503_REG_CONTROL 0x04
21+
#define AW37503_REG_WPRTEN 0x21
22+
23+
#define AW37503_VOUT_MASK 0x1F
24+
#define AW37503_VOUT_N_VOLTAGE 0x15
25+
#define AW37503_VOUT_VMIN 4000000
26+
#define AW37503_VOUT_VMAX 6000000
27+
#define AW37503_VOUT_STEP 100000
28+
29+
#define AW37503_REG_APPS_DIS_VPOS BIT(1)
30+
#define AW37503_REG_APPS_DIS_VNEG BIT(0)
31+
32+
#define AW37503_REGULATOR_ID_VPOS 0
33+
#define AW37503_REGULATOR_ID_VNEG 1
34+
#define AW37503_MAX_REGULATORS 2
35+
36+
struct aw37503_reg_pdata {
37+
struct gpio_desc *en_gpiod;
38+
int ena_gpio_state;
39+
};
40+
41+
struct aw37503_regulator {
42+
struct device *dev;
43+
struct aw37503_reg_pdata reg_pdata[AW37503_MAX_REGULATORS];
44+
};
45+
46+
static int aw37503_regulator_enable(struct regulator_dev *rdev)
47+
{
48+
struct aw37503_regulator *chip = rdev_get_drvdata(rdev);
49+
int id = rdev_get_id(rdev);
50+
struct aw37503_reg_pdata *rpdata = &chip->reg_pdata[id];
51+
int ret;
52+
53+
if (!IS_ERR(rpdata->en_gpiod)) {
54+
gpiod_set_value_cansleep(rpdata->en_gpiod, 1);
55+
rpdata->ena_gpio_state = 1;
56+
}
57+
58+
/* Hardware automatically enable discharge bit in enable */
59+
if (rdev->constraints->active_discharge ==
60+
REGULATOR_ACTIVE_DISCHARGE_DISABLE) {
61+
ret = regulator_set_active_discharge_regmap(rdev, false);
62+
if (ret < 0) {
63+
dev_err(chip->dev, "Failed to disable active discharge: %d\n",
64+
ret);
65+
return ret;
66+
}
67+
}
68+
69+
return 0;
70+
}
71+
72+
static int aw37503_regulator_disable(struct regulator_dev *rdev)
73+
{
74+
struct aw37503_regulator *chip = rdev_get_drvdata(rdev);
75+
int id = rdev_get_id(rdev);
76+
struct aw37503_reg_pdata *rpdata = &chip->reg_pdata[id];
77+
78+
if (!IS_ERR(rpdata->en_gpiod)) {
79+
gpiod_set_value_cansleep(rpdata->en_gpiod, 0);
80+
rpdata->ena_gpio_state = 0;
81+
}
82+
83+
return 0;
84+
}
85+
86+
static int aw37503_regulator_is_enabled(struct regulator_dev *rdev)
87+
{
88+
struct aw37503_regulator *chip = rdev_get_drvdata(rdev);
89+
int id = rdev_get_id(rdev);
90+
struct aw37503_reg_pdata *rpdata = &chip->reg_pdata[id];
91+
92+
if (!IS_ERR(rpdata->en_gpiod))
93+
return rpdata->ena_gpio_state;
94+
95+
return 1;
96+
}
97+
98+
static const struct regulator_ops aw37503_regulator_ops = {
99+
.enable = aw37503_regulator_enable,
100+
.disable = aw37503_regulator_disable,
101+
.is_enabled = aw37503_regulator_is_enabled,
102+
.list_voltage = regulator_list_voltage_linear,
103+
.map_voltage = regulator_map_voltage_linear,
104+
.get_voltage_sel = regulator_get_voltage_sel_regmap,
105+
.set_voltage_sel = regulator_set_voltage_sel_regmap,
106+
.set_active_discharge = regulator_set_active_discharge_regmap,
107+
};
108+
109+
static int aw37503_of_parse_cb(struct device_node *np,
110+
const struct regulator_desc *desc,
111+
struct regulator_config *config)
112+
{
113+
struct aw37503_regulator *chip = config->driver_data;
114+
struct aw37503_reg_pdata *rpdata = &chip->reg_pdata[desc->id];
115+
int ret;
116+
117+
rpdata->en_gpiod = devm_fwnode_gpiod_get(chip->dev, of_fwnode_handle(np),
118+
"enable", GPIOD_OUT_LOW,
119+
"enable");
120+
121+
if (IS_ERR(rpdata->en_gpiod)) {
122+
ret = PTR_ERR(rpdata->en_gpiod);
123+
124+
/* Ignore the error other than probe defer */
125+
if (ret == -EPROBE_DEFER)
126+
return ret;
127+
return 0;
128+
}
129+
130+
return 0;
131+
}
132+
133+
#define AW37503_REGULATOR_DESC(_id, _name) \
134+
[AW37503_REGULATOR_ID_##_id] = { \
135+
.name = "aw37503-"#_name, \
136+
.supply_name = "vin", \
137+
.id = AW37503_REGULATOR_ID_##_id, \
138+
.of_match = of_match_ptr(#_name), \
139+
.of_parse_cb = aw37503_of_parse_cb, \
140+
.ops = &aw37503_regulator_ops, \
141+
.n_voltages = AW37503_VOUT_N_VOLTAGE, \
142+
.min_uV = AW37503_VOUT_VMIN, \
143+
.uV_step = AW37503_VOUT_STEP, \
144+
.enable_time = 500, \
145+
.vsel_mask = AW37503_VOUT_MASK, \
146+
.vsel_reg = AW37503_REG_##_id, \
147+
.active_discharge_off = 0, \
148+
.active_discharge_on = AW37503_REG_APPS_DIS_##_id, \
149+
.active_discharge_mask = AW37503_REG_APPS_DIS_##_id, \
150+
.active_discharge_reg = AW37503_REG_APPS, \
151+
.type = REGULATOR_VOLTAGE, \
152+
.owner = THIS_MODULE, \
153+
}
154+
155+
static const struct regulator_desc aw_regs_desc[AW37503_MAX_REGULATORS] = {
156+
AW37503_REGULATOR_DESC(VPOS, outp),
157+
AW37503_REGULATOR_DESC(VNEG, outn),
158+
};
159+
160+
static const struct regmap_range aw37503_no_reg_ranges[] = {
161+
regmap_reg_range(AW37503_REG_CONTROL + 1,
162+
AW37503_REG_WPRTEN - 1),
163+
};
164+
165+
static const struct regmap_access_table aw37503_no_reg_table = {
166+
.no_ranges = aw37503_no_reg_ranges,
167+
.n_no_ranges = ARRAY_SIZE(aw37503_no_reg_ranges),
168+
};
169+
170+
static const struct regmap_config aw37503_regmap_config = {
171+
.reg_bits = 8,
172+
.val_bits = 8,
173+
.max_register = AW37503_REG_WPRTEN,
174+
.rd_table = &aw37503_no_reg_table,
175+
.wr_table = &aw37503_no_reg_table,
176+
};
177+
178+
static int aw37503_probe(struct i2c_client *client)
179+
{
180+
struct device *dev = &client->dev;
181+
struct aw37503_regulator *chip;
182+
struct regulator_dev *rdev;
183+
struct regmap *regmap;
184+
struct regulator_config config = { };
185+
int id;
186+
187+
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
188+
if (!chip)
189+
return -ENOMEM;
190+
191+
regmap = devm_regmap_init_i2c(client, &aw37503_regmap_config);
192+
if (IS_ERR(regmap))
193+
return dev_err_probe(dev, PTR_ERR(regmap),
194+
"Failed to init regmap\n");
195+
196+
i2c_set_clientdata(client, chip);
197+
chip->dev = dev;
198+
199+
config.regmap = regmap;
200+
config.dev = dev;
201+
config.driver_data = chip;
202+
203+
for (id = 0; id < AW37503_MAX_REGULATORS; ++id) {
204+
rdev = devm_regulator_register(dev, &aw_regs_desc[id],
205+
&config);
206+
if (IS_ERR(rdev))
207+
return dev_err_probe(dev, PTR_ERR(rdev),
208+
"Failed to register regulator %s\n",
209+
aw_regs_desc[id].name);
210+
}
211+
return 0;
212+
}
213+
214+
static const struct i2c_device_id aw37503_id[] = {
215+
{.name = "aw37503",},
216+
{},
217+
};
218+
MODULE_DEVICE_TABLE(i2c, aw37503_id);
219+
220+
static const struct of_device_id aw37503_of_match[] = {
221+
{.compatible = "awinic,aw37503",},
222+
{ /* Sentinel */ },
223+
};
224+
225+
MODULE_DEVICE_TABLE(of, aw37503_of_match);
226+
227+
static struct i2c_driver aw37503_i2c_driver = {
228+
.driver = {
229+
.name = "aw37503",
230+
.of_match_table = aw37503_of_match,
231+
},
232+
.probe_new = aw37503_probe,
233+
.id_table = aw37503_id,
234+
};
235+
236+
module_i2c_driver(aw37503_i2c_driver);
237+
238+
MODULE_DESCRIPTION("aw37503 regulator driver");
239+
MODULE_AUTHOR("Alec Li <like@awinic.com>");
240+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)