xref: /openbsd-src/sys/arch/arm64/dev/apliic.c (revision 97b909b357032a8ef8fbf06bc0516d33b1de443e)
1 /*	$OpenBSD: apliic.c,v 1.5 2022/12/10 18:43:48 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2021 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/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #define _I2C_PRIVATE
28 #include <dev/i2c/i2cvar.h>
29 
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/ofw_clock.h>
32 #include <dev/ofw/ofw_pinctrl.h>
33 #include <dev/ofw/ofw_power.h>
34 #include <dev/ofw/fdt.h>
35 
36 /* Registers. */
37 #define I2C_MTXFIFO		0x00
38 #define  I2C_MTXFIFO_DATA_MASK		(0xff << 0)
39 #define  I2C_MTXFIFO_START		(1 << 8)
40 #define  I2C_MTXFIFO_STOP		(1 << 9)
41 #define  I2C_MTXFIFO_READ		(1 << 10)
42 #define I2C_MRXFIFO		0x04
43 #define  I2C_MRXFIFO_DATA_MASK		(0xff << 0)
44 #define  I2C_MRXFIFO_EMPTY		(1 << 8)
45 #define I2C_SMSTA		0x14
46 #define  I2C_SMSTA_MTN			(1 << 21)
47 #define  I2C_SMSTA_XEN			(1 << 27)
48 #define  I2C_SMSTA_XBUSY		(1 << 28)
49 #define I2C_CTL			0x1c
50 #define  I2C_CTL_CLK_MASK		(0xff << 0)
51 #define  I2C_CTL_MTR			(1 << 9)
52 #define  I2C_CTL_MRR			(1 << 10)
53 #define  I2C_CTL_EN			(1 << 11)
54 #define I2C_REV			0x28
55 
56 #define HREAD4(sc, reg)							\
57 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
58 #define HWRITE4(sc, reg, val)						\
59 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
60 #define HSET4(sc, reg, bits)						\
61 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
62 #define HCLR4(sc, reg, bits)						\
63 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
64 
65 struct apliic_softc {
66 	struct device		sc_dev;
67 	bus_space_tag_t		sc_iot;
68 	bus_space_handle_t	sc_ioh;
69 
70 	int			sc_node;
71 	int			sc_hwrev;
72 	uint32_t		sc_clkdiv;
73 	struct i2c_controller	sc_ic;
74 };
75 
76 int apliic_match(struct device *, void *, void *);
77 void apliic_attach(struct device *, struct device *, void *);
78 
79 const struct cfattach	apliic_ca = {
80 	sizeof (struct apliic_softc), apliic_match, apliic_attach
81 };
82 
83 struct cfdriver apliic_cd = {
84 	NULL, "apliic", DV_DULL
85 };
86 
87 int	apliic_acquire_bus(void *, int);
88 void	apliic_release_bus(void *, int);
89 int	apliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
90 	    void *, size_t, int);
91 
92 void	apliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
93 
94 int
apliic_match(struct device * parent,void * match,void * aux)95 apliic_match(struct device *parent, void *match, void *aux)
96 {
97 	struct fdt_attach_args *faa = aux;
98 
99 	return OF_is_compatible(faa->fa_node, "apple,i2c");
100 }
101 
102 void
apliic_attach(struct device * parent,struct device * self,void * aux)103 apliic_attach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct apliic_softc *sc = (struct apliic_softc *)self;
106 	struct fdt_attach_args *faa = aux;
107 	struct i2cbus_attach_args iba;
108 	uint32_t clock_speed, bus_speed;
109 
110 	if (faa->fa_nreg < 1) {
111 		printf(": no registers\n");
112 		return;
113 	}
114 
115 	sc->sc_iot = faa->fa_iot;
116 	sc->sc_node = faa->fa_node;
117 
118 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
119 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
120 		printf(": can't map registers\n");
121 		return;
122 	}
123 
124 	printf("\n");
125 
126 	pinctrl_byname(sc->sc_node, "default");
127 	power_domain_enable(sc->sc_node);
128 	clock_enable_all(sc->sc_node);
129 
130 	clock_speed = clock_get_frequency(sc->sc_node, NULL);
131 	bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000) * 16;
132 	sc->sc_clkdiv = (clock_speed + bus_speed - 1) / bus_speed;
133 	KASSERT(sc->sc_clkdiv <= I2C_CTL_CLK_MASK);
134 
135 	sc->sc_hwrev = HREAD4(sc, I2C_REV);
136 
137 	HWRITE4(sc, I2C_CTL, sc->sc_clkdiv | I2C_CTL_MTR | I2C_CTL_MRR |
138 	    (sc->sc_hwrev >= 6 ? I2C_CTL_EN : 0));
139 
140 	sc->sc_ic.ic_cookie = sc;
141 	sc->sc_ic.ic_acquire_bus = apliic_acquire_bus;
142 	sc->sc_ic.ic_release_bus = apliic_release_bus;
143 	sc->sc_ic.ic_exec = apliic_exec;
144 
145 	/* Configure its children */
146 	memset(&iba, 0, sizeof(iba));
147 	iba.iba_name = "iic";
148 	iba.iba_tag = &sc->sc_ic;
149 	iba.iba_bus_scan = apliic_bus_scan;
150 	iba.iba_bus_scan_arg = &sc->sc_node;
151 
152 	config_found(&sc->sc_dev, &iba, iicbus_print);
153 }
154 
155 int
apliic_wait(struct apliic_softc * sc)156 apliic_wait(struct apliic_softc *sc)
157 {
158 	uint32_t reg;
159 	int timo;
160 
161 	for (timo = 100; timo > 0; timo--) {
162 		reg = HREAD4(sc, I2C_SMSTA);
163 		if (reg & I2C_SMSTA_XEN)
164 			break;
165 		delay(1000);
166 	}
167 	if (reg & I2C_SMSTA_MTN)
168 		return ENXIO;
169 	if (timo == 0) {
170 		HWRITE4(sc, I2C_SMSTA, reg);
171 		return ETIMEDOUT;
172 	}
173 
174 	HWRITE4(sc, I2C_SMSTA, I2C_SMSTA_XEN);
175 	return 0;
176 }
177 
178 int
apliic_acquire_bus(void * cookie,int flags)179 apliic_acquire_bus(void *cookie, int flags)
180 {
181 	return 0;
182 }
183 
184 void
apliic_release_bus(void * cookie,int flags)185 apliic_release_bus(void *cookie, int flags)
186 {
187 }
188 
189 int
apliic_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * buf,size_t buflen,int flags)190 apliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
191     size_t cmdlen, void *buf, size_t buflen, int flags)
192 {
193 	struct apliic_softc *sc = cookie;
194 	uint32_t reg;
195 	int error, i;
196 
197 	if (!I2C_OP_STOP_P(op))
198 		return EINVAL;
199 
200 	reg = HREAD4(sc, I2C_SMSTA);
201 	HWRITE4(sc, I2C_SMSTA, reg);
202 
203 	if (cmdlen > 0) {
204 		HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1);
205 		for (i = 0; i < cmdlen - 1; i++)
206 			HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[i]);
207 		HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[cmdlen - 1] |
208 		    (buflen == 0 ? I2C_MTXFIFO_STOP : 0));
209 	}
210 
211 	if (buflen == 0)
212 		return 0;
213 
214 	if (I2C_OP_READ_P(op)) {
215 		HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1 | 1);
216 		HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_READ | buflen |
217 		    I2C_MTXFIFO_STOP);
218 		error = apliic_wait(sc);
219 		if (error)
220 			return error;
221 		for (i = 0; i < buflen; i++) {
222 			reg = HREAD4(sc, I2C_MRXFIFO);
223 			if (reg & I2C_MRXFIFO_EMPTY)
224 				return EIO;
225 			((uint8_t *)buf)[i] = reg & I2C_MRXFIFO_DATA_MASK;
226 		}
227 	} else {
228 		if (cmdlen == 0)
229 			HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1);
230 		for (i = 0; i < buflen - 1; i++)
231 			HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[i]);
232 		HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[buflen - 1] |
233 		    I2C_MTXFIFO_STOP);
234 		error = apliic_wait(sc);
235 		if (error)
236 			return error;
237 	}
238 
239 	return 0;
240 }
241 
242 void
apliic_bus_scan(struct device * self,struct i2cbus_attach_args * iba,void * arg)243 apliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
244 {
245 	int iba_node = *(int *)arg;
246 	struct i2c_attach_args ia;
247 	char status[32];
248 	char *compat;
249 	uint32_t reg[1];
250 	int node;
251 	int len;
252 
253 	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
254 		memset(status, 0, sizeof(status));
255 		if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
256 		    strcmp(status, "disabled") == 0)
257 			continue;
258 
259 		memset(reg, 0, sizeof(reg));
260 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
261 			continue;
262 
263 		len = OF_getproplen(node, "compatible");
264 		if (len <= 0)
265 			continue;
266 
267 		compat = malloc(len, M_TEMP, M_WAITOK);
268 		OF_getprop(node, "compatible", compat, len);
269 
270 		memset(&ia, 0, sizeof(ia));
271 		ia.ia_tag = iba->iba_tag;
272 		ia.ia_addr = bemtoh32(&reg[0]);
273 		ia.ia_name = compat;
274 		ia.ia_namelen = len;
275 		ia.ia_cookie = &node;
276 		config_found(self, &ia, iic_print);
277 
278 		free(compat, M_TEMP, len);
279 	}
280 }
281