Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/as3722.c
39483 views
1
/*-
2
* Copyright (c) 2016 Michal Meloun <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/cdefs.h>
28
/*
29
* AS3722 PMIC driver
30
*/
31
32
#include <sys/param.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/gpio.h>
36
#include <sys/kernel.h>
37
#include <sys/module.h>
38
#include <sys/malloc.h>
39
#include <sys/rman.h>
40
#include <sys/sx.h>
41
42
#include <machine/bus.h>
43
44
#include <dev/regulator/regulator.h>
45
#include <dev/fdt/fdt_pinctrl.h>
46
#include <dev/gpio/gpiobusvar.h>
47
#include <dev/iicbus/iiconf.h>
48
#include <dev/iicbus/iicbus.h>
49
#include <dev/ofw/ofw_bus.h>
50
#include <dev/ofw/ofw_bus_subr.h>
51
52
#include <dt-bindings/mfd/as3722.h>
53
54
#include "clock_if.h"
55
#include "regdev_if.h"
56
57
#include "as3722.h"
58
59
static struct ofw_compat_data compat_data[] = {
60
{"ams,as3722", 1},
61
{NULL, 0},
62
};
63
64
#define LOCK(_sc) sx_xlock(&(_sc)->lock)
65
#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock)
66
#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "as3722")
67
#define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock);
68
#define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED);
69
#define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED);
70
71
#define AS3722_DEVICE_ID 0x0C
72
73
/*
74
* Raw register access function.
75
*/
76
int
77
as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val)
78
{
79
uint8_t addr;
80
int rv;
81
struct iic_msg msgs[2] = {
82
{0, IIC_M_WR, 1, &addr},
83
{0, IIC_M_RD, 1, val},
84
};
85
86
msgs[0].slave = sc->bus_addr;
87
msgs[1].slave = sc->bus_addr;
88
addr = reg;
89
90
rv = iicbus_transfer(sc->dev, msgs, 2);
91
if (rv != 0) {
92
device_printf(sc->dev,
93
"Error when reading reg 0x%02X, rv: %d\n", reg, rv);
94
return (EIO);
95
}
96
97
return (0);
98
}
99
100
int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
101
size_t size)
102
{
103
uint8_t addr;
104
int rv;
105
struct iic_msg msgs[2] = {
106
{0, IIC_M_WR, 1, &addr},
107
{0, IIC_M_RD, size, buf},
108
};
109
110
msgs[0].slave = sc->bus_addr;
111
msgs[1].slave = sc->bus_addr;
112
addr = reg;
113
114
rv = iicbus_transfer(sc->dev, msgs, 2);
115
if (rv != 0) {
116
device_printf(sc->dev,
117
"Error when reading reg 0x%02X, rv: %d\n", reg, rv);
118
return (EIO);
119
}
120
121
return (0);
122
}
123
124
int
125
as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val)
126
{
127
uint8_t data[2];
128
int rv;
129
130
struct iic_msg msgs[1] = {
131
{0, IIC_M_WR, 2, data},
132
};
133
134
msgs[0].slave = sc->bus_addr;
135
data[0] = reg;
136
data[1] = val;
137
138
rv = iicbus_transfer(sc->dev, msgs, 1);
139
if (rv != 0) {
140
device_printf(sc->dev,
141
"Error when writing reg 0x%02X, rv: %d\n", reg, rv);
142
return (EIO);
143
}
144
return (0);
145
}
146
147
int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
148
size_t size)
149
{
150
uint8_t data[1];
151
int rv;
152
struct iic_msg msgs[2] = {
153
{0, IIC_M_WR, 1, data},
154
{0, IIC_M_WR | IIC_M_NOSTART, size, buf},
155
};
156
157
msgs[0].slave = sc->bus_addr;
158
msgs[1].slave = sc->bus_addr;
159
data[0] = reg;
160
161
rv = iicbus_transfer(sc->dev, msgs, 2);
162
if (rv != 0) {
163
device_printf(sc->dev,
164
"Error when writing reg 0x%02X, rv: %d\n", reg, rv);
165
return (EIO);
166
}
167
return (0);
168
}
169
170
int
171
as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set)
172
{
173
uint8_t val;
174
int rv;
175
176
rv = as3722_read(sc, reg, &val);
177
if (rv != 0)
178
return (rv);
179
180
val &= ~clear;
181
val |= set;
182
183
rv = as3722_write(sc, reg, val);
184
if (rv != 0)
185
return (rv);
186
187
return (0);
188
}
189
190
static int
191
as3722_get_version(struct as3722_softc *sc)
192
{
193
uint8_t reg;
194
int rv;
195
196
/* Verify AS3722 ID and version. */
197
rv = RD1(sc, AS3722_ASIC_ID1, &reg);
198
if (rv != 0)
199
return (ENXIO);
200
201
if (reg != AS3722_DEVICE_ID) {
202
device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg);
203
return (ENXIO);
204
}
205
206
rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev);
207
if (rv != 0)
208
return (ENXIO);
209
210
if (bootverbose)
211
device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev);
212
return (0);
213
}
214
215
static int
216
as3722_init(struct as3722_softc *sc)
217
{
218
uint32_t reg;
219
int rv;
220
221
reg = 0;
222
if (sc->int_pullup)
223
reg |= AS3722_INT_PULL_UP;
224
if (sc->i2c_pullup)
225
reg |= AS3722_I2C_PULL_UP;
226
227
rv = RM1(sc, AS3722_IO_VOLTAGE,
228
AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg);
229
if (rv != 0)
230
return (ENXIO);
231
232
/* mask interrupts */
233
rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0);
234
if (rv != 0)
235
return (ENXIO);
236
rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0);
237
if (rv != 0)
238
return (ENXIO);
239
rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0);
240
if (rv != 0)
241
return (ENXIO);
242
rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0);
243
if (rv != 0)
244
return (ENXIO);
245
return (0);
246
}
247
248
static int
249
as3722_parse_fdt(struct as3722_softc *sc, phandle_t node)
250
{
251
252
sc->int_pullup =
253
OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0;
254
sc->i2c_pullup =
255
OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0;
256
return 0;
257
}
258
259
static void
260
as3722_intr(void *arg)
261
{
262
/* XXX Finish temperature alarms. */
263
}
264
265
static int
266
as3722_probe(device_t dev)
267
{
268
269
if (!ofw_bus_status_okay(dev))
270
return (ENXIO);
271
272
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
273
return (ENXIO);
274
275
device_set_desc(dev, "AS3722 PMIC");
276
return (BUS_PROBE_DEFAULT);
277
}
278
279
static int
280
as3722_attach(device_t dev)
281
{
282
struct as3722_softc *sc;
283
int rv, rid;
284
phandle_t node;
285
286
sc = device_get_softc(dev);
287
sc->dev = dev;
288
sc->bus_addr = iicbus_get_addr(dev);
289
node = ofw_bus_get_node(sc->dev);
290
rv = 0;
291
LOCK_INIT(sc);
292
293
rid = 0;
294
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
295
RF_ACTIVE);
296
if (sc->irq_res == NULL) {
297
device_printf(dev, "Cannot allocate interrupt.\n");
298
rv = ENXIO;
299
goto fail;
300
}
301
302
rv = as3722_parse_fdt(sc, node);
303
if (rv != 0)
304
goto fail;
305
rv = as3722_get_version(sc);
306
if (rv != 0)
307
goto fail;
308
rv = as3722_init(sc);
309
if (rv != 0)
310
goto fail;
311
rv = as3722_regulator_attach(sc, node);
312
if (rv != 0)
313
goto fail;
314
rv = as3722_gpio_attach(sc, node);
315
if (rv != 0)
316
goto fail;
317
rv = as3722_rtc_attach(sc, node);
318
if (rv != 0)
319
goto fail;
320
321
fdt_pinctrl_register(dev, NULL);
322
fdt_pinctrl_configure_by_name(dev, "default");
323
324
/* Setup interrupt. */
325
rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
326
NULL, as3722_intr, sc, &sc->irq_h);
327
if (rv) {
328
device_printf(dev, "Cannot setup interrupt.\n");
329
goto fail;
330
}
331
bus_attach_children(dev);
332
return (0);
333
334
fail:
335
if (sc->irq_h != NULL)
336
bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
337
if (sc->irq_res != NULL)
338
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
339
LOCK_DESTROY(sc);
340
return (rv);
341
}
342
343
static int
344
as3722_detach(device_t dev)
345
{
346
struct as3722_softc *sc;
347
int error;
348
349
error = bus_generic_detach(dev);
350
if (error != 0)
351
return (error);
352
353
sc = device_get_softc(dev);
354
if (sc->irq_h != NULL)
355
bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
356
if (sc->irq_res != NULL)
357
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
358
LOCK_DESTROY(sc);
359
360
return (0);
361
}
362
363
static phandle_t
364
as3722_gpio_get_node(device_t bus, device_t dev)
365
{
366
367
/* We only have one child, the GPIO bus, which needs our own node. */
368
return (ofw_bus_get_node(bus));
369
}
370
371
static device_method_t as3722_methods[] = {
372
/* Device interface */
373
DEVMETHOD(device_probe, as3722_probe),
374
DEVMETHOD(device_attach, as3722_attach),
375
DEVMETHOD(device_detach, as3722_detach),
376
377
/* Regdev interface */
378
DEVMETHOD(regdev_map, as3722_regulator_map),
379
380
/* RTC interface */
381
DEVMETHOD(clock_gettime, as3722_rtc_gettime),
382
DEVMETHOD(clock_settime, as3722_rtc_settime),
383
384
/* GPIO protocol interface */
385
DEVMETHOD(gpio_get_bus, as3722_gpio_get_bus),
386
DEVMETHOD(gpio_pin_max, as3722_gpio_pin_max),
387
DEVMETHOD(gpio_pin_getname, as3722_gpio_pin_getname),
388
DEVMETHOD(gpio_pin_getflags, as3722_gpio_pin_getflags),
389
DEVMETHOD(gpio_pin_getcaps, as3722_gpio_pin_getcaps),
390
DEVMETHOD(gpio_pin_setflags, as3722_gpio_pin_setflags),
391
DEVMETHOD(gpio_pin_get, as3722_gpio_pin_get),
392
DEVMETHOD(gpio_pin_set, as3722_gpio_pin_set),
393
DEVMETHOD(gpio_pin_toggle, as3722_gpio_pin_toggle),
394
DEVMETHOD(gpio_map_gpios, as3722_gpio_map_gpios),
395
396
/* fdt_pinctrl interface */
397
DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure),
398
399
/* ofw_bus interface */
400
DEVMETHOD(ofw_bus_get_node, as3722_gpio_get_node),
401
402
DEVMETHOD_END
403
};
404
405
static DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods,
406
sizeof(struct as3722_softc));
407
EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, NULL, NULL, 74);
408
409