1/* $NetBSD: auvitek_i2c.c,v 1.3 2011/10/02 16:30:58 jmcneill 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.3 2011/10/02 16:30:58 jmcneill Exp $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/device.h>
39#include <sys/conf.h>
40#include <sys/bus.h>
41#include <sys/module.h>
42
43#include <dev/usb/usb.h>
44#include <dev/usb/usbdi.h>
45#include <dev/usb/usbdi_util.h>
46#include <dev/usb/usbdevs.h>
47
48#include <dev/i2c/i2cvar.h>
49
50#include <dev/usb/auvitekreg.h>
51#include <dev/usb/auvitekvar.h>
52
53/* #define AUVITEK_I2C_DEBUG */
54
55static int auvitek_i2c_acquire_bus(void *, int);
56static void auvitek_i2c_release_bus(void *, int);
57static int auvitek_i2c_exec(void *, i2c_op_t, i2c_addr_t,
58 const void *, size_t, void *, size_t, int);
59
60static int auvitek_i2c_read(struct auvitek_softc *, i2c_addr_t,
61 uint8_t *, size_t);
62static int auvitek_i2c_write(struct auvitek_softc *, i2c_addr_t,
63 const uint8_t *, size_t);
64static bool auvitek_i2c_wait(struct auvitek_softc *);
65static bool auvitek_i2c_wait_rdack(struct auvitek_softc *);
66static bool auvitek_i2c_wait_rddone(struct auvitek_softc *);
67static bool auvitek_i2c_wait_wrdone(struct auvitek_softc *);
68
69int
70auvitek_i2c_attach(struct auvitek_softc *sc)
71{
72 mutex_init(&sc->sc_i2c_lock, MUTEX_DEFAULT, IPL_NONE);
73 sc->sc_i2c.ic_cookie = sc;
74 sc->sc_i2c.ic_acquire_bus = auvitek_i2c_acquire_bus;
75 sc->sc_i2c.ic_release_bus = auvitek_i2c_release_bus;
76 sc->sc_i2c.ic_exec = auvitek_i2c_exec;
77
78 auvitek_i2c_rescan(sc, NULL, NULL);
79
80 return 0;
81}
82
83int
84auvitek_i2c_detach(struct auvitek_softc *sc, int flags)
85{
86 mutex_destroy(&sc->sc_i2c_lock);
87
88 if (sc->sc_i2cdev)
89 config_detach(sc->sc_i2cdev, flags);
90
91 return 0;
92}
93
94void
95auvitek_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_type = I2C_TYPE_SMBUS;
104 iba.iba_tag = &sc->sc_i2c;
105 sc->sc_i2cdev = config_found_ia(sc->sc_dev, "i2cbus",
106 &iba, iicbus_print);
107 }
108#endif
109}
110
111void
112auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child)
113{
114 if (sc->sc_i2cdev == child)
115 sc->sc_i2cdev = NULL;
116}
117
118static int
119auvitek_i2c_acquire_bus(void *opaque, int flags)
120{
121 struct auvitek_softc *sc = opaque;
122
123 if (flags & I2C_F_POLL) {
124 if (!mutex_tryenter(&sc->sc_i2c_lock))
125 return EBUSY;
126 } else {
127 mutex_enter(&sc->sc_i2c_lock);
128 }
129
130 return 0;
131}
132
133static void
134auvitek_i2c_release_bus(void *opaque, int flags)
135{
136 struct auvitek_softc *sc = opaque;
137
138 mutex_exit(&sc->sc_i2c_lock);
139}
140
141static int
142auvitek_i2c_exec(void *opaque, i2c_op_t op, i2c_addr_t addr,
143 const void *cmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
144{
145 struct auvitek_softc *sc = opaque;
146
147 if (I2C_OP_READ_P(op))
148 return auvitek_i2c_read(sc, addr, vbuf, buflen);
149 else
150 return auvitek_i2c_write(sc, addr, cmd, cmdlen);
151}
152
153static int
154auvitek_i2c_read(struct auvitek_softc *sc, i2c_addr_t addr,
155 uint8_t *buf, size_t buflen)
156{
157 uint8_t v;
158 unsigned int i;
159
160 //KASSERT(mutex_owned(&sc->sc_i2c_lock));
161
162 auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
163 auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
164 auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
165
166 if (buflen == 0) {
167 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
168 AU0828_I2C_TRIGGER_RD);
169 if (auvitek_i2c_wait_rdack(sc) == false)
170 return EBUSY;
171 return 0;
172 }
173
174 for (i = 0; i < buflen; i++) {
175 v = AU0828_I2C_TRIGGER_RD;
176 if (i < (buflen - 1))
177 v |= AU0828_I2C_TRIGGER_HOLD;
178 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
179
180 if (auvitek_i2c_wait_rddone(sc) == false)
181 return EBUSY;
182
183 buf[i] = auvitek_read_1(sc, AU0828_REG_I2C_FIFORD);
184 }
185
186 if (auvitek_i2c_wait(sc) == false)
187 return EBUSY;
188
189 return 0;
190}
191
192static int
193auvitek_i2c_write(struct auvitek_softc *sc, i2c_addr_t addr,
194 const uint8_t *buf, size_t buflen)
195{
196 uint8_t v;
197 unsigned int i, fifolen;
198
199 //KASSERT(mutex_owned(&sc->sc_i2c_lock));
200
201 auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
202 auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
203 auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1);
204
205 if (buflen == 0) {
206 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER,
207 AU0828_I2C_TRIGGER_RD);
208 if (auvitek_i2c_wait(sc) == false)
209 return EBUSY;
210 if (auvitek_i2c_wait_rdack(sc) == false)
211 return EBUSY;
212 return 0;
213 }
214
215 fifolen = 0;
216 for (i = 0; i < buflen; i++) {
217 v = AU0828_I2C_TRIGGER_WR;
218 if (i < (buflen - 1))
219 v |= AU0828_I2C_TRIGGER_HOLD;
220
221 auvitek_write_1(sc, AU0828_REG_I2C_FIFOWR, buf[i]);
222 ++fifolen;
223
224 if (fifolen == 4 || i == (buflen - 1)) {
225 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v);
226 fifolen = 0;
227
228 if (auvitek_i2c_wait_wrdone(sc) == false)
229 return EBUSY;
230 }
231 }
232
233 if (auvitek_i2c_wait(sc) == false)
234 return EBUSY;
235
236 return 0;
237}
238
239static bool
240auvitek_i2c_wait(struct auvitek_softc *sc)
241{
242 uint8_t status;
243 int retry = 1000;
244
245 while (--retry > 0) {
246 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
247 if (!(status & AU0828_I2C_STATUS_BUSY))
248 break;
249 delay(10);
250 }
251 if (retry == 0)
252 return false;
253
254 return true;
255}
256
257static bool
258auvitek_i2c_wait_rdack(struct auvitek_softc *sc)
259{
260 uint8_t status;
261 int retry = 1000;
262
263 while (--retry > 0) {
264 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
265 if (!(status & AU0828_I2C_STATUS_NO_RD_ACK))
266 break;
267 delay(10);
268 }
269 if (retry == 0)
270 return false;
271
272 return true;
273}
274
275static bool
276auvitek_i2c_wait_rddone(struct auvitek_softc *sc)
277{
278 uint8_t status;
279 int retry = 1000;
280
281 while (--retry > 0) {
282 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
283 if (status & AU0828_I2C_STATUS_RD_DONE)
284 break;
285 delay(10);
286 }
287 if (retry == 0)
288 return false;
289
290 return true;
291}
292
293static bool
294auvitek_i2c_wait_wrdone(struct auvitek_softc *sc)
295{
296 uint8_t status;
297 int retry = 1000;
298
299 while (--retry > 0) {
300 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS);
301 if (status & AU0828_I2C_STATUS_WR_DONE)
302 break;
303 delay(10);
304 }
305 if (retry == 0)
306 return false;
307
308 return true;
309}
310