xref: /netbsd-src/sys/dev/usb/auvitek_i2c.c (revision 6b71feb71da51fbb1ded3bcd4220b786dc77872a)
1 /* $NetBSD: auvitek_i2c.c,v 1.8 2022/03/13 12:49:36 riastradh 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.8 2022/03/13 12:49:36 riastradh 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
auvitek_i2c_attach(struct auvitek_softc * sc)72 auvitek_i2c_attach(struct auvitek_softc *sc)
73 {
74 
75 	iic_tag_init(&sc->sc_i2c);
76 	sc->sc_i2c.ic_cookie = sc;
77 	sc->sc_i2c.ic_exec = auvitek_i2c_exec;
78 
79 	auvitek_i2c_rescan(sc, NULL, NULL);
80 
81 	sc->sc_i2c_attached = true;
82 
83 	return 0;
84 }
85 
86 int
auvitek_i2c_detach(struct auvitek_softc * sc,int flags)87 auvitek_i2c_detach(struct auvitek_softc *sc, int flags)
88 {
89 
90 	if (!sc->sc_i2c_attached)
91 		return 0;
92 
93 	iic_tag_fini(&sc->sc_i2c);
94 
95 	return 0;
96 }
97 
98 void
auvitek_i2c_rescan(struct auvitek_softc * sc,const char * ifattr,const int * locs)99 auvitek_i2c_rescan(struct auvitek_softc *sc, const char *ifattr,
100     const int *locs)
101 {
102 #ifdef AUVITEK_I2C_DEBUG
103 	struct i2cbus_attach_args iba;
104 
105 	if (ifattr_match(ifattr, "i2cbus") && sc->sc_i2cdev == NULL) {
106 		memset(&iba, 0, sizeof(iba));
107 		iba.iba_tag = &sc->sc_i2c;
108 		sc->sc_i2cdev = config_found(sc->sc_dev, &iba, iicbus_print,
109 		    CFARGS(.iattr = "i2cbus"));
110 	}
111 #endif
112 }
113 
114 void
auvitek_i2c_childdet(struct auvitek_softc * sc,device_t child)115 auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child)
116 {
117 	if (sc->sc_i2cdev == child)
118 		sc->sc_i2cdev = NULL;
119 }
120 
121 static int
auvitek_i2c_exec(void * opaque,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * vbuf,size_t buflen,int flags)122 auvitek_i2c_exec(void *opaque, i2c_op_t op, i2c_addr_t addr,
123     const void *cmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
124 {
125 	struct auvitek_softc *sc = opaque;
126 
127 	if (I2C_OP_READ_P(op))
128 		return auvitek_i2c_read(sc, addr, vbuf, buflen);
129 	else
130 		return auvitek_i2c_write(sc, addr, cmd, cmdlen);
131 }
132 
133 static int
auvitek_i2c_read(struct auvitek_softc * sc,i2c_addr_t addr,uint8_t * buf,size_t buflen)134 auvitek_i2c_read(struct auvitek_softc *sc, i2c_addr_t addr,
135     uint8_t *buf, size_t buflen)
136 {
137 	uint8_t v;
138 	unsigned int i;
139 
140 	auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
141 	auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
142 	auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
143 
144 	if (buflen == 0) {
145 		auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
146 		    AU0828_I2C_TRIGGER_RD);
147 		if (auvitek_i2c_wait_rdack(sc) == false)
148 			return EBUSY;
149 		return 0;
150 	}
151 
152 	for (i = 0; i < buflen; i++) {
153 		v = AU0828_I2C_TRIGGER_RD;
154 		if (i < (buflen - 1))
155 			v |= AU0828_I2C_TRIGGER_HOLD;
156 		auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
157 
158 		if (auvitek_i2c_wait_rddone(sc) == false)
159 			return EBUSY;
160 
161 		buf[i] = auvitek_read_1(sc, AU0828_REG_I2C_FIFORD);
162 	}
163 
164 	if (auvitek_i2c_wait(sc) == false)
165 		return EBUSY;
166 
167 	return 0;
168 }
169 
170 static int
auvitek_i2c_write(struct auvitek_softc * sc,i2c_addr_t addr,const uint8_t * buf,size_t buflen)171 auvitek_i2c_write(struct auvitek_softc *sc, i2c_addr_t addr,
172     const uint8_t *buf, size_t buflen)
173 {
174 	uint8_t v;
175 	unsigned int i, fifolen;
176 
177 	auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
178 	auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
179 	auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
180 
181 	if (buflen == 0) {
182 		auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
183 		    AU0828_I2C_TRIGGER_RD);
184 		if (auvitek_i2c_wait(sc) == false)
185 			return EBUSY;
186 		if (auvitek_i2c_wait_rdack(sc) == false)
187 			return EBUSY;
188 		return 0;
189 	}
190 
191 	fifolen = 0;
192 	for (i = 0; i < buflen; i++) {
193 		v = AU0828_I2C_TRIGGER_WR;
194 		if (i < (buflen - 1))
195 			v |= AU0828_I2C_TRIGGER_HOLD;
196 
197 		auvitek_write_1(sc, AU0828_REG_I2C_FIFOWR, buf[i]);
198 		++fifolen;
199 
200 		if (fifolen == 4 || i == (buflen - 1)) {
201 			auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
202 			fifolen = 0;
203 
204 			if (auvitek_i2c_wait_wrdone(sc) == false)
205 				return EBUSY;
206 		}
207 	}
208 
209 	if (auvitek_i2c_wait(sc) == false)
210 		return EBUSY;
211 
212 	return 0;
213 }
214 
215 static bool
auvitek_i2c_wait(struct auvitek_softc * sc)216 auvitek_i2c_wait(struct auvitek_softc *sc)
217 {
218 	uint8_t status;
219 	int retry = 1000;
220 
221 	while (--retry > 0) {
222 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
223 		if (!(status & AU0828_I2C_STATUS_BUSY))
224 			break;
225 		delay(10);
226 	}
227 	if (retry == 0)
228 		return false;
229 
230 	return true;
231 }
232 
233 static bool
auvitek_i2c_wait_rdack(struct auvitek_softc * sc)234 auvitek_i2c_wait_rdack(struct auvitek_softc *sc)
235 {
236 	uint8_t status;
237 	int retry = 1000;
238 
239 	while (--retry > 0) {
240 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
241 		if (!(status & AU0828_I2C_STATUS_NO_RD_ACK))
242 			break;
243 		delay(10);
244 	}
245 	if (retry == 0)
246 		return false;
247 
248 	return true;
249 }
250 
251 static bool
auvitek_i2c_wait_rddone(struct auvitek_softc * sc)252 auvitek_i2c_wait_rddone(struct auvitek_softc *sc)
253 {
254 	uint8_t status;
255 	int retry = 1000;
256 
257 	while (--retry > 0) {
258 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
259 		if (status & AU0828_I2C_STATUS_RD_DONE)
260 			break;
261 		delay(10);
262 	}
263 	if (retry == 0)
264 		return false;
265 
266 	return true;
267 }
268 
269 static bool
auvitek_i2c_wait_wrdone(struct auvitek_softc * sc)270 auvitek_i2c_wait_wrdone(struct auvitek_softc *sc)
271 {
272 	uint8_t status;
273 	int retry = 1000;
274 
275 	while (--retry > 0) {
276 		status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
277 		if (status & AU0828_I2C_STATUS_WR_DONE)
278 			break;
279 		delay(10);
280 	}
281 	if (retry == 0)
282 		return false;
283 
284 	return true;
285 }
286