auvitek_i2c.c revision 1.9 1 /* $NetBSD: auvitek_i2c.c,v 1.9 2025/09/15 13:23:03 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) 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.9 2025/09/15 13:23:03 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
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
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
99 auvitek_i2c_rescan(struct auvitek_softc *sc, const char *ifattr,
100 const int *locs)
101 {
102 #ifdef AUVITEK_I2C_DEBUG
103 if (ifattr_match(ifattr, "i2cbus") && sc->sc_i2cdev == NULL) {
104 sc->sc_i2cdev = iicbus_attach(sc->sc_dev, &sc->sc_i2c);
105 }
106 #endif
107 }
108
109 void
110 auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child)
111 {
112 if (sc->sc_i2cdev == child)
113 sc->sc_i2cdev = NULL;
114 }
115
116 static int
117 auvitek_i2c_exec(void *opaque, i2c_op_t op, i2c_addr_t addr,
118 const void *cmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
119 {
120 struct auvitek_softc *sc = opaque;
121
122 if (I2C_OP_READ_P(op))
123 return auvitek_i2c_read(sc, addr, vbuf, buflen);
124 else
125 return auvitek_i2c_write(sc, addr, cmd, cmdlen);
126 }
127
128 static int
129 auvitek_i2c_read(struct auvitek_softc *sc, i2c_addr_t addr,
130 uint8_t *buf, size_t buflen)
131 {
132 uint8_t v;
133 unsigned int i;
134
135 auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
136 auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
137 auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
138
139 if (buflen == 0) {
140 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
141 AU0828_I2C_TRIGGER_RD);
142 if (auvitek_i2c_wait_rdack(sc) == false)
143 return EBUSY;
144 return 0;
145 }
146
147 for (i = 0; i < buflen; i++) {
148 v = AU0828_I2C_TRIGGER_RD;
149 if (i < (buflen - 1))
150 v |= AU0828_I2C_TRIGGER_HOLD;
151 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
152
153 if (auvitek_i2c_wait_rddone(sc) == false)
154 return EBUSY;
155
156 buf[i] = auvitek_read_1(sc, AU0828_REG_I2C_FIFORD);
157 }
158
159 if (auvitek_i2c_wait(sc) == false)
160 return EBUSY;
161
162 return 0;
163 }
164
165 static int
166 auvitek_i2c_write(struct auvitek_softc *sc, i2c_addr_t addr,
167 const uint8_t *buf, size_t buflen)
168 {
169 uint8_t v;
170 unsigned int i, fifolen;
171
172 auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
173 auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
174 auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
175
176 if (buflen == 0) {
177 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
178 AU0828_I2C_TRIGGER_RD);
179 if (auvitek_i2c_wait(sc) == false)
180 return EBUSY;
181 if (auvitek_i2c_wait_rdack(sc) == false)
182 return EBUSY;
183 return 0;
184 }
185
186 fifolen = 0;
187 for (i = 0; i < buflen; i++) {
188 v = AU0828_I2C_TRIGGER_WR;
189 if (i < (buflen - 1))
190 v |= AU0828_I2C_TRIGGER_HOLD;
191
192 auvitek_write_1(sc, AU0828_REG_I2C_FIFOWR, buf[i]);
193 ++fifolen;
194
195 if (fifolen == 4 || i == (buflen - 1)) {
196 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
197 fifolen = 0;
198
199 if (auvitek_i2c_wait_wrdone(sc) == false)
200 return EBUSY;
201 }
202 }
203
204 if (auvitek_i2c_wait(sc) == false)
205 return EBUSY;
206
207 return 0;
208 }
209
210 static bool
211 auvitek_i2c_wait(struct auvitek_softc *sc)
212 {
213 uint8_t status;
214 int retry = 1000;
215
216 while (--retry > 0) {
217 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
218 if (!(status & AU0828_I2C_STATUS_BUSY))
219 break;
220 delay(10);
221 }
222 if (retry == 0)
223 return false;
224
225 return true;
226 }
227
228 static bool
229 auvitek_i2c_wait_rdack(struct auvitek_softc *sc)
230 {
231 uint8_t status;
232 int retry = 1000;
233
234 while (--retry > 0) {
235 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
236 if (!(status & AU0828_I2C_STATUS_NO_RD_ACK))
237 break;
238 delay(10);
239 }
240 if (retry == 0)
241 return false;
242
243 return true;
244 }
245
246 static bool
247 auvitek_i2c_wait_rddone(struct auvitek_softc *sc)
248 {
249 uint8_t status;
250 int retry = 1000;
251
252 while (--retry > 0) {
253 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
254 if (status & AU0828_I2C_STATUS_RD_DONE)
255 break;
256 delay(10);
257 }
258 if (retry == 0)
259 return false;
260
261 return true;
262 }
263
264 static bool
265 auvitek_i2c_wait_wrdone(struct auvitek_softc *sc)
266 {
267 uint8_t status;
268 int retry = 1000;
269
270 while (--retry > 0) {
271 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
272 if (status & AU0828_I2C_STATUS_WR_DONE)
273 break;
274 delay(10);
275 }
276 if (retry == 0)
277 return false;
278
279 return true;
280 }
281