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