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
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
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
86int
87auvitek_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
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_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
114void
115auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child)
116{
117	if (sc->sc_i2cdev == child)
118		sc->sc_i2cdev = NULL;
119}
120
121static int
122auvitek_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
133static int
134auvitek_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
170static int
171auvitek_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
215static bool
216auvitek_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
233static bool
234auvitek_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
251static bool
252auvitek_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
269static bool
270auvitek_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