xref: /openbsd-src/sys/dev/fdt/amliic.c (revision c020cf82e0cc147236f01a8dca7052034cf9d30d)
1 /*	$OpenBSD: amliic.c,v 1.1 2019/10/07 18:54:03 kettenis 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 	int i = 0, j = 0;
177 	int timo, k;
178 
179 #define SET_TOKEN(i, t) \
180 	tokens |= (((uint64_t)(t)) << ((i) * 4))
181 #define SET_DATA(i, d) \
182 	data |= (((uint64_t)(d)) << ((i) * 8))
183 
184 	if (cmdlen + buflen > 8)
185 		return EINVAL;
186 
187 	if (cmdlen > 0) {
188 		SET_TOKEN(i++, START);
189 		SET_TOKEN(i++, SLAVE_ADDR_WRITE);
190 		for (k = 0; k < cmdlen; k++) {
191 			SET_TOKEN(i++, DATA);
192 			SET_DATA(j++, ((uint8_t *)cmd)[k]);
193 		}
194 	}
195 
196 	if (I2C_OP_READ_P(op)) {
197 		SET_TOKEN(i++, START);
198 		SET_TOKEN(i++, SLAVE_ADDR_READ);
199 		for (k = 0; k < buflen; k++)
200 			SET_TOKEN(i++, (k == (buflen - 1)) ? DATA_LAST : DATA);
201 	} else {
202 		if (cmdlen == 0) {
203 			SET_TOKEN(i++, START);
204 			SET_TOKEN(i++, SLAVE_ADDR_WRITE);
205 		}
206 		for (k = 0; k < buflen; k++) {
207 			SET_TOKEN(i++, DATA);
208 			SET_DATA(j++, ((uint8_t *)buf)[k]);
209 		}
210 	}
211 
212 	if (I2C_OP_STOP_P(op))
213 		SET_TOKEN(i++, STOP);
214 
215 	SET_TOKEN(i++, END);
216 
217 	/* Write slave address, tokens and associated data to hardware. */
218 	HWRITE4(sc, I2C_M_SLAVE_ADDRESS, addr << 1);
219 	HWRITE4(sc, I2C_M_TOKEN_LIST0, tokens);
220 	HWRITE4(sc, I2C_M_TOKEN_LIST1, tokens >> 32);
221 	HWRITE4(sc, I2C_M_TOKEN_WDATA0, data);
222 	HWRITE4(sc, I2C_M_TOKEN_WDATA1, data >> 32);
223 
224 	/* Start token list processing. */
225 	HSET4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START);
226 	for (timo = 50000; timo > 0; timo--) {
227 		if ((HREAD4(sc, I2C_M_CONTROL) & I2C_M_CONTROL_STATUS) == 0)
228 			break;
229 		delay(10);
230 	}
231 	HCLR4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START);
232 	if (timo == 0 || HREAD4(sc, I2C_M_CONTROL) & I2C_M_CONTROL_ERROR)
233 		return EIO;
234 
235 	if (I2C_OP_READ_P(op)) {
236 		for (i = 0; i < buflen; i++) {
237 			if (i % 4 == 0)
238 				rdata = HREAD4(sc, I2C_M_TOKEN_RDATA0 + i);
239 			((uint8_t *)buf)[i] = rdata;
240 			rdata >>= 8;
241 		}
242 	}
243 
244 	return 0;
245 }
246 
247 void
248 amliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
249 {
250 	int iba_node = *(int *)arg;
251 	struct i2c_attach_args ia;
252 	char name[32];
253 	uint32_t reg[1];
254 	int node;
255 
256 	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
257 		memset(name, 0, sizeof(name));
258 		memset(reg, 0, sizeof(reg));
259 
260 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
261 			continue;
262 		if (name[0] == '\0')
263 			continue;
264 
265 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
266 			continue;
267 
268 		memset(&ia, 0, sizeof(ia));
269 		ia.ia_tag = iba->iba_tag;
270 		ia.ia_addr = bemtoh32(&reg[0]);
271 		ia.ia_name = name;
272 		ia.ia_cookie = &node;
273 		config_found(self, &ia, iic_print);
274 	}
275 }
276