1 /* $OpenBSD: qcpmic.c,v 1.3 2025/01/03 14:13:55 kettenis Exp $ */
2 /*
3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/malloc.h>
20 #include <sys/systm.h>
21 #include <sys/timeout.h>
22
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25
26 #include <dev/fdt/spmivar.h>
27
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/fdt.h>
30
31 /* PMIC Registers. */
32 #define PMIC_REV2 0x101
33 #define PMIC_REV3 0x102
34 #define PMIC_REV4 0x103
35 #define PMIC_TYPE 0x104
36 #define PMIC_TYPE_VAL 0x51
37 #define PMIC_SUBTYPE 0x105
38 #define PMIC_FAB_ID 0x1f2
39
40 struct qcpmic_softc {
41 struct device sc_dev;
42 int sc_node;
43
44 spmi_tag_t sc_tag;
45 int8_t sc_sid;
46
47 void *sc_ih;
48
49 struct timeout sc_tick;
50 };
51
52 int qcpmic_match(struct device *, void *, void *);
53 void qcpmic_attach(struct device *, struct device *, void *);
54
55 const struct cfattach qcpmic_ca = {
56 sizeof(struct qcpmic_softc), qcpmic_match, qcpmic_attach
57 };
58
59 struct cfdriver qcpmic_cd = {
60 NULL, "qcpmic", DV_DULL
61 };
62
63 uint8_t qcpmic_read(struct qcpmic_softc *, uint16_t);
64
65 int
qcpmic_match(struct device * parent,void * match,void * aux)66 qcpmic_match(struct device *parent, void *match, void *aux)
67 {
68 struct spmi_attach_args *saa = aux;
69
70 return OF_is_compatible(saa->sa_node, "qcom,spmi-pmic");
71 }
72
73 void
qcpmic_attach(struct device * parent,struct device * self,void * aux)74 qcpmic_attach(struct device *parent, struct device *self, void *aux)
75 {
76 struct spmi_attach_args *saa = aux;
77 struct qcpmic_softc *sc = (struct qcpmic_softc *)self;
78 struct spmi_attach_args sa;
79 char name[32];
80 int node;
81
82 sc->sc_node = saa->sa_node;
83 sc->sc_tag = saa->sa_tag;
84 sc->sc_sid = saa->sa_sid;
85
86 if (qcpmic_read(sc, PMIC_TYPE) != PMIC_TYPE_VAL) {
87 printf(": unknown PMIC type\n");
88 return;
89 }
90
91 printf("\n");
92
93 for (node = OF_child(saa->sa_node); node; node = OF_peer(node)) {
94 memset(name, 0, sizeof(name));
95 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
96 continue;
97 if (name[0] == '\0')
98 continue;
99
100 memset(&sa, 0, sizeof(sa));
101 sa.sa_tag = sc->sc_tag;
102 sa.sa_sid = sc->sc_sid;
103 sa.sa_name = name;
104 sa.sa_node = node;
105 config_found(self, &sa, NULL);
106 }
107 }
108
109 uint8_t
qcpmic_read(struct qcpmic_softc * sc,uint16_t addr)110 qcpmic_read(struct qcpmic_softc *sc, uint16_t addr)
111 {
112 uint8_t reg = 0;
113 int err;
114
115 err = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
116 addr, ®, sizeof(reg));
117 if (err)
118 printf("%s: error (%u) reading 0x%x", sc->sc_dev.dv_xname,
119 err, addr);
120
121 return reg;
122 }
123