hc
2025-02-14 bbb9540dc49f70f6b703d1c8d1b85fa5f602d86e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
 *
 * Author: Shunqing Chen <csq@rock-chips.com>
 */
 
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
 
#define WL2868C_DEVICE_D1        0x00
#define WL2868C_DEVICE_D2        0x01
#define WL2868C_DISCHARGE_RESISTORS    0x02
#define WL2868C_LDO1_VOUT        0x03
#define WL2868C_LDO2_VOUT        0x04
#define WL2868C_LDO3_VOUT        0x05
#define WL2868C_LDO4_VOUT        0x06
#define WL2868C_LDO5_VOUT        0x07
#define WL2868C_LDO6_VOUT        0x08
#define WL2868C_LDO7_VOUT        0x09
#define WL2868C_LDO1_LDO2_SEQ        0x0a
#define WL2868C_LDO3_LDO4_SEQ        0x0b
#define WL2868C_LDO5_LDO6_SEQ        0x0c
#define WL2868C_LDO7_SEQ        0x0d
#define WL2868C_LDO_EN            0x0e
#define WL2868C_SEQ_STATUS        0x0f
#define WL2868C_LDO1_STATUS        0x10
#define WL2868C_LDO1_OCP_CTL        0x11
#define WL2868C_LDO2_STATUS        0x12
#define WL2868C_LDO2_OCP_CTL        0x13
#define WL2868C_LDO3_STATUS        0x14
#define WL2868C_LDO3_OCP_CTL        0x15
#define WL2868C_LDO4_STATUS        0x16
#define WL2868C_LDO4_OCP_CTL        0x17
#define WL2868C_LDO5_STATUS        0x18
#define WL2868C_LDO5_OCP_CTL        0x19
#define WL2868C_LDO6_STATUS        0x1a
#define WL2868C_LDO6_OCP_CTL        0x1b
#define WL2868C_LDO7_STATUS        0x1c
#define WL2868C_LDO7_OCP_CTL        0x1d
#define WL2868C_REPROGRAMMABLE_I2C_ADDR    0x1e
#define RESERVED_1            0x1f
#define INT_LATCHED_CLR            0x20
#define INT_EN_SET            0x21
#define INT_LATCHED_STS            0x22
#define INT_PENDING_STS            0x23
#define UVLO_CTL            0x24
#define RESERVED_2            0x25
 
#define WL2868C_VSEL_MASK        0xff
 
enum wl2868c_regulators {
   WL2868C_REGULATOR_LDO1 = 0,
   WL2868C_REGULATOR_LDO2,
   WL2868C_REGULATOR_LDO3,
   WL2868C_REGULATOR_LDO4,
   WL2868C_REGULATOR_LDO5,
   WL2868C_REGULATOR_LDO6,
   WL2868C_REGULATOR_LDO7,
   WL2868C_MAX_REGULATORS,
};
 
struct wl2868c {
   struct device *dev;
   struct regmap *regmap;
   struct regulator_dev *rdev;
   struct gpio_desc *reset_gpio;
   int min_dropout_uv;
   int ldo_vout[7];
   int ldo_en;
};
 
static const struct regulator_ops wl2868c_reg_ops = {
   .list_voltage        = regulator_list_voltage_linear,
   .map_voltage        = regulator_map_voltage_linear,
   .get_voltage_sel    = regulator_get_voltage_sel_regmap,
   .set_voltage_sel    = regulator_set_voltage_sel_regmap,
   .enable            = regulator_enable_regmap,
   .disable        = regulator_disable_regmap,
   .is_enabled        = regulator_is_enabled_regmap,
};
 
#define WL2868C_DESC(_id, _match, _supply, _min, _max, _step, _vreg,    \
   _vmask, _ereg, _emask, _enval, _disval)        \
   {                                \
       .name        = (_match),                \
       .supply_name    = (_supply),                \
       .of_match    = of_match_ptr(_match),            \
       .regulators_node = of_match_ptr("regulators"),        \
       .type        = REGULATOR_VOLTAGE,            \
       .id        = (_id),                \
       .n_voltages    = (((_max) - (_min)) / (_step) + 1),    \
       .owner        = THIS_MODULE,                \
       .min_uV        = (_min) * 1000,            \
       .uV_step    = (_step) * 1000,            \
       .vsel_reg    = (_vreg),                \
       .vsel_mask    = (_vmask),                \
       .enable_reg    = (_ereg),                \
       .enable_mask    = (_emask),                \
       .enable_val     = (_enval),                \
       .disable_val     = (_disval),                \
       .ops        = &wl2868c_reg_ops,            \
   }
 
static const struct regulator_desc wl2868c_reg[] = {
   WL2868C_DESC(WL2868C_REGULATOR_LDO1, "WL_LDO1", "ldo1", 496, 2536, 8,
            WL2868C_LDO1_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(0), BIT(0), 0),
   WL2868C_DESC(WL2868C_REGULATOR_LDO2, "WL_LDO2", "ldo2", 496, 2536, 8,
            WL2868C_LDO2_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(1), BIT(1), 0),
   WL2868C_DESC(WL2868C_REGULATOR_LDO3, "WL_LDO3", "ldo3", 1504, 3544, 8,
            WL2868C_LDO3_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(2), BIT(2), 0),
   WL2868C_DESC(WL2868C_REGULATOR_LDO4, "WL_LDO4", "ldo4", 1504, 3544, 8,
            WL2868C_LDO4_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(3), BIT(3), 0),
   WL2868C_DESC(WL2868C_REGULATOR_LDO5, "WL_LDO5", "ldo5", 1504, 3544, 8,
            WL2868C_LDO5_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(4), BIT(4), 0),
   WL2868C_DESC(WL2868C_REGULATOR_LDO6, "WL_LDO6", "ldo6", 1504, 3544, 8,
            WL2868C_LDO6_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(5), BIT(5), 0),
   WL2868C_DESC(WL2868C_REGULATOR_LDO7, "WL_LDO7", "ldo7", 1504, 3544, 8,
            WL2868C_LDO7_VOUT, WL2868C_VSEL_MASK, WL2868C_LDO_EN, BIT(6), BIT(6), 0),
};
 
static const struct regmap_range wl2868c_writeable_ranges[] = {
   regmap_reg_range(WL2868C_DISCHARGE_RESISTORS, WL2868C_SEQ_STATUS),
   regmap_reg_range(WL2868C_LDO1_OCP_CTL, WL2868C_LDO1_OCP_CTL),
   regmap_reg_range(WL2868C_LDO2_OCP_CTL, WL2868C_LDO2_OCP_CTL),
   regmap_reg_range(WL2868C_LDO3_OCP_CTL, WL2868C_LDO3_OCP_CTL),
   regmap_reg_range(WL2868C_LDO4_OCP_CTL, WL2868C_LDO4_OCP_CTL),
   regmap_reg_range(WL2868C_LDO5_OCP_CTL, WL2868C_LDO5_OCP_CTL),
   regmap_reg_range(WL2868C_LDO6_OCP_CTL, WL2868C_LDO6_OCP_CTL),
   regmap_reg_range(WL2868C_LDO7_OCP_CTL, WL2868C_REPROGRAMMABLE_I2C_ADDR),
   regmap_reg_range(INT_LATCHED_CLR, INT_LATCHED_CLR),
   regmap_reg_range(INT_EN_SET, INT_EN_SET),
   regmap_reg_range(UVLO_CTL, UVLO_CTL),
};
 
static const struct regmap_range wl2868c_readable_ranges[] = {
   regmap_reg_range(WL2868C_DEVICE_D1, RESERVED_2),
};
 
static const struct regmap_range wl2868c_volatile_ranges[] = {
   regmap_reg_range(WL2868C_DISCHARGE_RESISTORS, WL2868C_SEQ_STATUS),
   regmap_reg_range(WL2868C_LDO1_OCP_CTL, WL2868C_LDO1_OCP_CTL),
   regmap_reg_range(WL2868C_LDO2_OCP_CTL, WL2868C_LDO2_OCP_CTL),
   regmap_reg_range(WL2868C_LDO3_OCP_CTL, WL2868C_LDO3_OCP_CTL),
   regmap_reg_range(WL2868C_LDO4_OCP_CTL, WL2868C_LDO4_OCP_CTL),
   regmap_reg_range(WL2868C_LDO5_OCP_CTL, WL2868C_LDO5_OCP_CTL),
   regmap_reg_range(WL2868C_LDO6_OCP_CTL, WL2868C_LDO6_OCP_CTL),
   regmap_reg_range(WL2868C_LDO7_OCP_CTL, WL2868C_REPROGRAMMABLE_I2C_ADDR),
   regmap_reg_range(INT_LATCHED_CLR, INT_LATCHED_CLR),
   regmap_reg_range(INT_EN_SET, INT_EN_SET),
   regmap_reg_range(UVLO_CTL, UVLO_CTL),
};
 
static const struct regmap_access_table wl2868c_writeable_table = {
   .yes_ranges   = wl2868c_writeable_ranges,
   .n_yes_ranges = ARRAY_SIZE(wl2868c_writeable_ranges),
};
 
static const struct regmap_access_table wl2868c_readable_table = {
   .yes_ranges   = wl2868c_readable_ranges,
   .n_yes_ranges = ARRAY_SIZE(wl2868c_readable_ranges),
};
 
static const struct regmap_access_table wl2868c_volatile_table = {
   .yes_ranges   = wl2868c_volatile_ranges,
   .n_yes_ranges = ARRAY_SIZE(wl2868c_volatile_ranges),
};
 
static const struct regmap_config wl2868c_regmap_config = {
   .reg_bits = 8,
   .val_bits = 8,
   .max_register = RESERVED_2,
   .wr_table = &wl2868c_writeable_table,
   .rd_table = &wl2868c_readable_table,
   .cache_type = REGCACHE_RBTREE,
   .volatile_table = &wl2868c_volatile_table,
};
 
static void wl2868c_reset(struct wl2868c *wl2868c)
{
   gpiod_set_value_cansleep(wl2868c->reset_gpio, 0);
   usleep_range(10000, 11000);
   gpiod_set_value_cansleep(wl2868c->reset_gpio, 1);
   usleep_range(10000, 11000);
   gpiod_set_value_cansleep(wl2868c->reset_gpio, 0);
   usleep_range(10000, 11000);
}
 
static int wl2868c_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
   struct device *dev = &client->dev;
   struct regulator_config config = {};
   struct regulator_dev *rdev;
   const struct regulator_desc *regulators;
   struct wl2868c *wl2868c;
   int ret, i;
 
   wl2868c = devm_kzalloc(dev, sizeof(struct wl2868c), GFP_KERNEL);
   if (!wl2868c)
       return -ENOMEM;
 
   wl2868c->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
   if (IS_ERR(wl2868c->reset_gpio)) {
       ret = PTR_ERR(wl2868c->reset_gpio);
       dev_err(dev, "failed to request reset GPIO: %d\n", ret);
       return ret;
   }
 
   wl2868c_reset(wl2868c);
 
   i2c_set_clientdata(client, wl2868c);
   wl2868c->dev = dev;
   wl2868c->regmap = devm_regmap_init_i2c(client, &wl2868c_regmap_config);
   if (IS_ERR(wl2868c->regmap)) {
       ret = PTR_ERR(wl2868c->regmap);
       dev_err(dev, "Failed to allocate register map: %d\n", ret);
       return ret;
   }
 
   config.dev = &client->dev;
   config.regmap = wl2868c->regmap;
   regulators = wl2868c_reg;
   /* Instantiate the regulators */
   for (i = 0; i < WL2868C_MAX_REGULATORS; i++) {
       rdev = devm_regulator_register(&client->dev,
                          &regulators[i], &config);
       if (IS_ERR(rdev)) {
           dev_err(&client->dev,
               "failed to register %d regulator\n", i);
           return PTR_ERR(rdev);
       }
   }
 
   return 0;
}
 
static void wl2868c_regulator_shutdown(struct i2c_client *client)
{
   struct wl2868c *wl2868c = i2c_get_clientdata(client);
 
   if (system_state == SYSTEM_POWER_OFF)
       regmap_write(wl2868c->regmap, WL2868C_LDO_EN, 0x80);
}
 
static int __maybe_unused wl2868c_suspend(struct device *dev)
{
   struct i2c_client *client = to_i2c_client(dev);
   struct wl2868c *wl2868c = i2c_get_clientdata(client);
   int i;
 
   regmap_read(wl2868c->regmap, WL2868C_LDO_EN, &wl2868c->ldo_en);
   for (i = 0; i < ARRAY_SIZE(wl2868c->ldo_vout); i++)
       regmap_read(wl2868c->regmap, WL2868C_LDO1_VOUT + i,
               &wl2868c->ldo_vout[i]);
 
   return 0;
}
 
static int __maybe_unused wl2868c_resume(struct device *dev)
{
   struct i2c_client *client = to_i2c_client(dev);
   struct wl2868c *wl2868c = i2c_get_clientdata(client);
   int i;
 
   wl2868c_reset(wl2868c);
   for (i = 0; i < ARRAY_SIZE(wl2868c->ldo_vout); i++)
       regmap_write(wl2868c->regmap, WL2868C_LDO1_VOUT + i,
                wl2868c->ldo_vout[i]);
   regmap_write(wl2868c->regmap, WL2868C_LDO_EN, wl2868c->ldo_en);
 
   return 0;
}
 
static SIMPLE_DEV_PM_OPS(wl2868c_pm_ops, wl2868c_suspend, wl2868c_resume);
 
static const struct i2c_device_id wl2868c_i2c_id[] = {
   { "wl2868c", 0 },
   { }
};
 
MODULE_DEVICE_TABLE(i2c, wl2868c_i2c_id);
 
static const struct of_device_id wl2868c_of_match[] = {
   { .compatible = "willsemi,wl2868c" },
   {}
};
MODULE_DEVICE_TABLE(of, wl2868c_of_match);
 
static struct i2c_driver wl2868c_i2c_driver = {
   .driver = {
       .name = "wl2868c",
       .of_match_table = of_match_ptr(wl2868c_of_match),
       .pm = &wl2868c_pm_ops,
   },
   .id_table = wl2868c_i2c_id,
   .probe    = wl2868c_i2c_probe,
   .shutdown = wl2868c_regulator_shutdown,
};
 
module_i2c_driver(wl2868c_i2c_driver);
 
MODULE_DESCRIPTION("WL2868C regulator driver");
MODULE_AUTHOR("Shunqing Chen <csq@rock-chips.com>");
MODULE_LICENSE("GPL");