xref: /netbsd-src/sys/dev/usb/auvitek_i2c.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /* $NetBSD: auvitek_i2c.c,v 1.5 2019/12/22 23:23:32 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Auvitek AU0828 USB controller - I2C access ops
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: auvitek_i2c.c,v 1.5 2019/12/22 23:23:32 thorpej Exp $");
35 
36 #ifdef _KERNEL_OPT
37 #include "opt_usb.h"
38 #endif
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/device.h>
43 #include <sys/conf.h>
44 #include <sys/bus.h>
45 #include <sys/module.h>
46 
47 #include <dev/usb/usb.h>
48 #include <dev/usb/usbdi.h>
49 #include <dev/usb/usbdi_util.h>
50 #include <dev/usb/usbdevs.h>
51 
52 #include <dev/i2c/i2cvar.h>
53 
54 #include <dev/usb/auvitekreg.h>
55 #include <dev/usb/auvitekvar.h>
56 
57 /* #define AUVITEK_I2C_DEBUG */
58 
59 static int	auvitek_i2c_exec(void *, i2c_op_t, i2c_addr_t,
60 				 const void *, size_t, void *, size_t, int);
61 
62 static int	auvitek_i2c_read(struct auvitek_softc *, i2c_addr_t,
63 				 uint8_t *, size_t);
64 static int	auvitek_i2c_write(struct auvitek_softc *, i2c_addr_t,
65 				  const uint8_t *, size_t);
66 static bool	auvitek_i2c_wait(struct auvitek_softc *);
67 static bool	auvitek_i2c_wait_rdack(struct auvitek_softc *);
68 static bool	auvitek_i2c_wait_rddone(struct auvitek_softc *);
69 static bool	auvitek_i2c_wait_wrdone(struct auvitek_softc *);
70 
71 int
72 auvitek_i2c_attach(struct auvitek_softc *sc)
73 {
74 	iic_tag_init(&sc->sc_i2c);
75 	sc->sc_i2c.ic_cookie = sc;
76 	sc->sc_i2c.ic_exec = auvitek_i2c_exec;
77 
78 	auvitek_i2c_rescan(sc, NULL, NULL);
79 
80 	return 0;
81 }
82 
83 int
84 auvitek_i2c_detach(struct auvitek_softc *sc, int flags)
85 {
86 	iic_tag_fini(&sc->sc_i2c);
87 
88 	if (sc->sc_i2cdev)
89 		config_detach(sc->sc_i2cdev, flags);
90 
91 	return 0;
92 }
93 
94 void
95 auvitek_i2c_rescan(struct auvitek_softc *sc, const char *ifattr,
96     const int *locs)
97 {
98 #ifdef AUVITEK_I2C_DEBUG
99 	struct i2cbus_attach_args iba;
100 
101 	if (ifattr_match(ifattr, "i2cbus") && sc->sc_i2cdev == NULL) {
102 		memset(&iba, 0, sizeof(iba));
103 		iba.iba_tag = &sc->sc_i2c;
104 		sc->sc_i2cdev = config_found_ia(sc->sc_dev, "i2cbus",
105 		    &iba, iicbus_print);
106 	}
107 #endif
108 }
109 
110 void
111 auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child)
112 {
113 	if (sc->sc_i2cdev == child)
114 		sc->sc_i2cdev = NULL;
115 }
116 
117 static int
118 auvitek_i2c_exec(void *opaque, i2c_op_t op, i2c_addr_t addr,
119     const void *cmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
120 {
121 	struct auvitek_softc *sc = opaque;
122 
123 	if (I2C_OP_READ_P(op))
124 		return auvitek_i2c_read(sc, addr, vbuf, buflen);
125 	else
126 		return auvitek_i2c_write(sc, addr, cmd, cmdlen);
127 }
128 
129 static int
130 auvitek_i2c_read(struct auvitek_softc *sc, i2c_addr_t addr,
131     uint8_t *buf, size_t buflen)
132 {
133 	uint8_t v;
134 	unsigned int i;
135 
136 	auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
137 	auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
138 	auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
139 
140 	if (buflen == 0) {
141 		auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
142 		    AU0828_I2C_TRIGGER_RD);
143 		if (auvitek_i2c_wait_rdack(sc) == false)
144 			return EBUSY;
145 		return 0;
146 	}
147 
148 	for (i = 0; i < buflen; i++) {
149 		v = AU0828_I2C_TRIGGER_RD;
150 		if (i < (buflen - 1))
151 			v |= AU0828_I2C_TRIGGER_HOLD;
152 		auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
153 
154 		if (auvitek_i2c_wait_rddone(sc) == false)
155 			return EBUSY;
156 
157 		buf[i] = auvitek_read_1(sc, AU0828_REG_I2C_FIFORD);
158 	}
159 
160 	if (auvitek_i2c_wait(sc) == false)
161 		return EBUSY;
162 
163 	return 0;
164 }
165 
166 static int
167 auvitek_i2c_write(struct auvitek_softc *sc, i2c_addr_t addr,
168     const uint8_t *buf, size_t buflen)
169 {
170 	uint8_t v;
171 	unsigned int i, fifolen;
172 
173 	auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
174 	auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
175 	auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
176 
177 	if (buflen == 0) {
178 		auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
179 		    AU0828_I2C_TRIGGER_RD);
180 		if (auvitek_i2c_wait(sc) == false)
181 			return EBUSY;
182 		if (auvitek_i2c_wait_rdack(sc) == false)
183 			return EBUSY;
184 		return 0;
185 	}
186 
187 	fifolen = 0;
188 	for (i = 0; i < buflen; i++) {
189 		v = AU0828_I2C_TRIGGER_WR;
190 		if (i < (buflen - 1))
191 			v |= AU0828_I2C_TRIGGER_HOLD;
192 
193 		auvitek_write_1(sc, AU0828_REG_I2C_FIFOWR, buf[i]);
194 		++fifolen;
195 
196 		if (fifolen == 4 || i == (buflen - 1)) {
197 			auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
198 			fifolen = 0;
199 
200 			if (auvitek_i2c_wait_wrdone(sc) == false)
201 				return EBUSY;
202 		}
203 	}
204 
205 	if (auvitek_i2c_wait(sc) == false)
206 		return EBUSY;
207 
208 	return 0;
209 }
210 
211 static bool
212 auvitek_i2c_wait(struct auvitek_softc *sc)
213 {
214 	uint8_t status;
215 	int retry = 1000;
216 
217 	while (--retry > 0) {
218 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
219 		if (!(status & AU0828_I2C_STATUS_BUSY))
220 			break;
221 		delay(10);
222 	}
223 	if (retry == 0)
224 		return false;
225 
226 	return true;
227 }
228 
229 static bool
230 auvitek_i2c_wait_rdack(struct auvitek_softc *sc)
231 {
232 	uint8_t status;
233 	int retry = 1000;
234 
235 	while (--retry > 0) {
236 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
237 		if (!(status & AU0828_I2C_STATUS_NO_RD_ACK))
238 			break;
239 		delay(10);
240 	}
241 	if (retry == 0)
242 		return false;
243 
244 	return true;
245 }
246 
247 static bool
248 auvitek_i2c_wait_rddone(struct auvitek_softc *sc)
249 {
250 	uint8_t status;
251 	int retry = 1000;
252 
253 	while (--retry > 0) {
254 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
255 		if (status & AU0828_I2C_STATUS_RD_DONE)
256 			break;
257 		delay(10);
258 	}
259 	if (retry == 0)
260 		return false;
261 
262 	return true;
263 }
264 
265 static bool
266 auvitek_i2c_wait_wrdone(struct auvitek_softc *sc)
267 {
268 	uint8_t status;
269 	int retry = 1000;
270 
271 	while (--retry > 0) {
272 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
273 		if (status & AU0828_I2C_STATUS_WR_DONE)
274 			break;
275 		delay(10);
276 	}
277 	if (retry == 0)
278 		return false;
279 
280 	return true;
281 }
282