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