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