xref: /openbsd/sys/dev/fdt/amliic.c (revision 771fbea0)
1 /*	$OpenBSD: amliic.c,v 1.3 2021/03/11 09:15:25 patrick Exp $	*/
2 /*
3  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
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 
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #define _I2C_PRIVATE
27 #include <dev/i2c/i2cvar.h>
28 
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/ofw_clock.h>
31 #include <dev/ofw/ofw_pinctrl.h>
32 #include <dev/ofw/fdt.h>
33 
34 /* Registers. */
35 #define I2C_M_CONTROL			0x00
36 #define  I2C_M_CONTROL_QTR_CLK_DLY_SHIFT	12
37 #define  I2C_M_CONTROL_QTR_CLK_EXT_SHIFT	28
38 #define  I2C_M_CONTROL_ERROR			(1 << 3)
39 #define  I2C_M_CONTROL_STATUS			(1 << 2)
40 #define  I2C_M_CONTROL_START			(1 << 0)
41 #define I2C_M_SLAVE_ADDRESS		0x04
42 #define I2C_M_TOKEN_LIST0		0x08
43 #define I2C_M_TOKEN_LIST1		0x0c
44 #define I2C_M_TOKEN_WDATA0		0x10
45 #define I2C_M_TOKEN_WDATA1		0x14
46 #define I2C_M_TOKEN_RDATA0		0x18
47 #define I2C_M_TOKEN_RDATA1		0x1c
48 
49 /* Token definitions. */
50 #define END				0x0
51 #define START				0x1
52 #define SLAVE_ADDR_WRITE		0x2
53 #define SLAVE_ADDR_READ			0x3
54 #define DATA				0x4
55 #define DATA_LAST			0x5
56 #define STOP				0x6
57 
58 #define HREAD4(sc, reg)							\
59 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
60 #define HWRITE4(sc, reg, val)						\
61 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
62 #define HSET4(sc, reg, bits)						\
63 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
64 #define HCLR4(sc, reg, bits)						\
65 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
66 
67 struct amliic_softc {
68 	struct device		sc_dev;
69 	bus_space_tag_t		sc_iot;
70 	bus_space_handle_t	sc_ioh;
71 
72 	int			sc_node;
73 	struct i2c_controller	sc_ic;
74 };
75 
76 int amliic_match(struct device *, void *, void *);
77 void amliic_attach(struct device *, struct device *, void *);
78 
79 struct cfattach	amliic_ca = {
80 	sizeof (struct amliic_softc), amliic_match, amliic_attach
81 };
82 
83 struct cfdriver amliic_cd = {
84 	NULL, "amliic", DV_DULL
85 };
86 
87 int	amliic_acquire_bus(void *, int);
88 void	amliic_release_bus(void *, int);
89 int	amliic_send_start(void *, int);
90 int	amliic_send_stop(void *, int);
91 int	amliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
92 	    void *, size_t, int);
93 
94 void	amliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
95 
96 int
97 amliic_match(struct device *parent, void *match, void *aux)
98 {
99 	struct fdt_attach_args *faa = aux;
100 
101 	return OF_is_compatible(faa->fa_node, "amlogic,meson-axg-i2c");
102 }
103 
104 void
105 amliic_attach(struct device *parent, struct device *self, void *aux)
106 {
107 	struct amliic_softc *sc = (struct amliic_softc *)self;
108 	struct fdt_attach_args *faa = aux;
109 	struct i2cbus_attach_args iba;
110 	uint32_t clock_speed, bus_speed;
111 	uint32_t div, divl, divh;
112 
113 	if (faa->fa_nreg < 1) {
114 		printf(": no registers\n");
115 		return;
116 	}
117 
118 	sc->sc_iot = faa->fa_iot;
119 	sc->sc_node = faa->fa_node;
120 
121 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
122 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
123 		printf(": can't map registers\n");
124 		return;
125 	}
126 
127 	printf("\n");
128 
129 	pinctrl_byname(sc->sc_node, "default");
130 
131 	clock_enable_all(sc->sc_node);
132 
133 	clock_speed = clock_get_frequency(sc->sc_node, NULL);
134 	bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000);
135 
136 	div = clock_speed / bus_speed / 4;
137 	divl = div & 0x3ff;
138 	divh = div >> 10;
139 	HWRITE4(sc, I2C_M_CONTROL, divh << I2C_M_CONTROL_QTR_CLK_EXT_SHIFT |
140 	    divl << I2C_M_CONTROL_QTR_CLK_DLY_SHIFT);
141 
142 	sc->sc_ic.ic_cookie = sc;
143 	sc->sc_ic.ic_acquire_bus = amliic_acquire_bus;
144 	sc->sc_ic.ic_release_bus = amliic_release_bus;
145 	sc->sc_ic.ic_exec = amliic_exec;
146 
147 	/* Configure its children */
148 	memset(&iba, 0, sizeof(iba));
149 	iba.iba_name = "iic";
150 	iba.iba_tag = &sc->sc_ic;
151 	iba.iba_bus_scan = amliic_bus_scan;
152 	iba.iba_bus_scan_arg = &sc->sc_node;
153 
154 	config_found(&sc->sc_dev, &iba, iicbus_print);
155 }
156 
157 int
158 amliic_acquire_bus(void *cookie, int flags)
159 {
160 	return 0;
161 }
162 
163 void
164 amliic_release_bus(void *cookie, int flags)
165 {
166 }
167 
168 int
169 amliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
170     size_t cmdlen, void *buf, size_t buflen, int flags)
171 {
172 	struct amliic_softc *sc = cookie;
173 	uint64_t tokens = 0;
174 	uint64_t data = 0;
175 	uint32_t rdata;
176 	uint32_t ctrl;
177 	size_t pos, len;
178 	int i = 0, j = 0;
179 	int timo, k;
180 
181 #define SET_TOKEN(i, t) \
182 	tokens |= (((uint64_t)(t)) << ((i) * 4))
183 #define SET_DATA(i, d) \
184 	data |= (((uint64_t)(d)) << ((i) * 8))
185 
186 	if (cmdlen > 8)
187 		return EINVAL;
188 
189 	if (cmdlen > 0) {
190 		SET_TOKEN(i++, START);
191 		SET_TOKEN(i++, SLAVE_ADDR_WRITE);
192 		for (k = 0; k < cmdlen; k++) {
193 			SET_TOKEN(i++, DATA);
194 			SET_DATA(j++, ((uint8_t *)cmd)[k]);
195 		}
196 	}
197 
198 	if (I2C_OP_READ_P(op)) {
199 		SET_TOKEN(i++, START);
200 		SET_TOKEN(i++, SLAVE_ADDR_READ);
201 	} else if (cmdlen == 0) {
202 		SET_TOKEN(i++, START);
203 		SET_TOKEN(i++, SLAVE_ADDR_WRITE);
204 	}
205 
206 	HWRITE4(sc, I2C_M_SLAVE_ADDRESS, addr << 1);
207 
208 	pos = 0;
209 	while (pos < buflen) {
210 		len = MIN(buflen - pos, 8 - j);
211 
212 		if (I2C_OP_READ_P(op)) {
213 			for (k = 0; k < len; k++)
214 				SET_TOKEN(i++, (pos == (buflen - 1)) ?
215 				    DATA_LAST : DATA);
216 		} else {
217 			for (k = 0; k < len; k++) {
218 				SET_TOKEN(i++, DATA);
219 				SET_DATA(j++, ((uint8_t *)buf)[pos++]);
220 			}
221 		}
222 
223 		if (pos == buflen && I2C_OP_STOP_P(op))
224 			SET_TOKEN(i++, STOP);
225 
226 		SET_TOKEN(i++, END);
227 
228 		/* Write slave address, tokens and data to hardware. */
229 		HWRITE4(sc, I2C_M_TOKEN_LIST0, tokens);
230 		HWRITE4(sc, I2C_M_TOKEN_LIST1, tokens >> 32);
231 		HWRITE4(sc, I2C_M_TOKEN_WDATA0, data);
232 		HWRITE4(sc, I2C_M_TOKEN_WDATA1, data >> 32);
233 
234 		/* Start token list processing. */
235 		HSET4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START);
236 		for (timo = 50000; timo > 0; timo--) {
237 			ctrl = HREAD4(sc, I2C_M_CONTROL);
238 			if ((ctrl & I2C_M_CONTROL_STATUS) == 0)
239 				break;
240 			delay(10);
241 		}
242 		HCLR4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START);
243 		if (ctrl & I2C_M_CONTROL_ERROR)
244 			return EIO;
245 		if (timo == 0)
246 			return ETIMEDOUT;
247 
248 		if (I2C_OP_READ_P(op)) {
249 			rdata = HREAD4(sc, I2C_M_TOKEN_RDATA0);
250 			for (i = 0; i < len; i++) {
251 				if (i == 4)
252 					rdata = HREAD4(sc, I2C_M_TOKEN_RDATA1);
253 				((uint8_t *)buf)[pos++] = rdata;
254 				rdata >>= 8;
255 			}
256 		}
257 
258 		/* Reset tokens. */
259 		tokens = 0;
260 		data = 0;
261 		i = j = 0;
262 	}
263 
264 	return 0;
265 }
266 
267 void
268 amliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
269 {
270 	int iba_node = *(int *)arg;
271 	struct i2c_attach_args ia;
272 	char name[32], status[32];
273 	uint32_t reg[1];
274 	int node;
275 
276 	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
277 		memset(name, 0, sizeof(name));
278 		memset(status, 0, sizeof(status));
279 		memset(reg, 0, sizeof(reg));
280 
281 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
282 			continue;
283 		if (name[0] == '\0')
284 			continue;
285 
286 		if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
287 		    strcmp(status, "disabled") == 0)
288 			continue;
289 
290 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
291 			continue;
292 
293 		memset(&ia, 0, sizeof(ia));
294 		ia.ia_tag = iba->iba_tag;
295 		ia.ia_addr = bemtoh32(&reg[0]);
296 		ia.ia_name = name;
297 		ia.ia_cookie = &node;
298 		config_found(self, &ia, iic_print);
299 	}
300 }
301