1183840Sraj/*-
2183840Sraj * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
3183840Sraj * All rights reserved.
4183840Sraj *
5183840Sraj * Developed by Semihalf.
6183840Sraj *
7183840Sraj * Redistribution and use in source and binary forms, with or without
8183840Sraj * modification, are permitted provided that the following conditions
9183840Sraj * are met:
10183840Sraj * 1. Redistributions of source code must retain the above copyright
11183840Sraj *    notice, this list of conditions and the following disclaimer.
12183840Sraj * 2. Redistributions in binary form must reproduce the above copyright
13183840Sraj *    notice, this list of conditions and the following disclaimer in the
14183840Sraj *    documentation and/or other materials provided with the distribution.
15183840Sraj * 3. Neither the name of MARVELL nor the names of contributors
16183840Sraj *    may be used to endorse or promote products derived from this software
17183840Sraj *    without specific prior written permission.
18183840Sraj *
19183840Sraj * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20183840Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21183840Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22183840Sraj * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23183840Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24183840Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25183840Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26183840Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27183840Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28183840Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29183840Sraj * SUCH DAMAGE.
30183840Sraj */
31183840Sraj
32183840Sraj/*
33183840Sraj * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell
34183840Sraj * SoCs. Supports master operation only, and works in polling mode.
35183840Sraj *
36183840Sraj * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software
37183840Sraj * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices".
38183840Sraj */
39183840Sraj
40183840Sraj#include <sys/cdefs.h>
41183840Sraj__FBSDID("$FreeBSD: releng/10.2/sys/arm/mv/twsi.c 266152 2014-05-15 16:11:06Z ian $");
42183840Sraj
43183840Sraj#include <sys/param.h>
44183840Sraj#include <sys/systm.h>
45183840Sraj#include <sys/bus.h>
46183840Sraj#include <sys/kernel.h>
47183840Sraj#include <sys/module.h>
48183840Sraj#include <sys/resource.h>
49183840Sraj
50239508Shrs#include <machine/_inttypes.h>
51183840Sraj#include <machine/bus.h>
52183840Sraj#include <machine/resource.h>
53183840Sraj
54183840Sraj#include <sys/rman.h>
55183840Sraj
56183840Sraj#include <sys/lock.h>
57183840Sraj#include <sys/mutex.h>
58183840Sraj
59183840Sraj#include <dev/iicbus/iiconf.h>
60183840Sraj#include <dev/iicbus/iicbus.h>
61239508Shrs#include <dev/fdt/fdt_common.h>
62209131Sraj#include <dev/ofw/ofw_bus.h>
63209131Sraj#include <dev/ofw/ofw_bus_subr.h>
64209131Sraj
65239508Shrs#include <arm/mv/mvreg.h>
66239508Shrs#include <arm/mv/mvvar.h>
67239508Shrs
68183840Sraj#include "iicbus_if.h"
69183840Sraj
70183840Sraj#define MV_TWSI_NAME		"twsi"
71239508Shrs#define	IICBUS_DEVNAME		"iicbus"
72183840Sraj
73183840Sraj#define TWSI_SLAVE_ADDR		0x00
74183840Sraj#define TWSI_EXT_SLAVE_ADDR	0x10
75183840Sraj#define TWSI_DATA		0x04
76183840Sraj
77183840Sraj#define TWSI_CONTROL		0x08
78183840Sraj#define TWSI_CONTROL_ACK	(1 << 2)
79183840Sraj#define TWSI_CONTROL_IFLG	(1 << 3)
80183840Sraj#define TWSI_CONTROL_STOP	(1 << 4)
81183840Sraj#define TWSI_CONTROL_START	(1 << 5)
82183840Sraj#define TWSI_CONTROL_TWSIEN	(1 << 6)
83183840Sraj#define TWSI_CONTROL_INTEN	(1 << 7)
84183840Sraj
85183840Sraj#define TWSI_STATUS			0x0c
86183840Sraj#define TWSI_STATUS_START		0x08
87183840Sraj#define TWSI_STATUS_RPTD_START		0x10
88183840Sraj#define TWSI_STATUS_ADDR_W_ACK		0x18
89183840Sraj#define TWSI_STATUS_DATA_WR_ACK		0x28
90183840Sraj#define TWSI_STATUS_ADDR_R_ACK		0x40
91183840Sraj#define TWSI_STATUS_DATA_RD_ACK		0x50
92183840Sraj#define TWSI_STATUS_DATA_RD_NOACK	0x58
93183840Sraj
94183840Sraj#define TWSI_BAUD_RATE		0x0c
95239508Shrs#define	TWSI_BAUD_RATE_PARAM(M,N)	((((M) << 3) | ((N) & 0x7)) & 0x7f)
96239508Shrs#define	TWSI_BAUD_RATE_RAW(C,M,N)	((C)/((10*(M+1))<<(N+1)))
97239508Shrs#define	TWSI_BAUD_RATE_SLOW		50000	/* 50kHz */
98239508Shrs#define	TWSI_BAUD_RATE_FAST		100000	/* 100kHz */
99183840Sraj
100183840Sraj#define TWSI_SOFT_RESET		0x1c
101183840Sraj
102183840Sraj#define TWSI_DEBUG
103183840Sraj#undef TWSI_DEBUG
104183840Sraj
105183840Sraj#ifdef  TWSI_DEBUG
106183840Sraj#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
107183840Sraj#else
108183840Sraj#define debugf(fmt, args...)
109183840Sraj#endif
110183840Sraj
111183840Srajstruct mv_twsi_softc {
112183840Sraj	device_t	dev;
113183840Sraj	struct resource	*res[1];	/* SYS_RES_MEMORY */
114183840Sraj	struct mtx	mutex;
115183840Sraj	device_t	iicbus;
116183840Sraj};
117183840Sraj
118239508Shrsstatic struct mv_twsi_baud_rate {
119239508Shrs	uint32_t	raw;
120239508Shrs	int		param;
121239508Shrs	int		m;
122239508Shrs	int		n;
123239508Shrs} baud_rate[IIC_FASTEST + 1];
124239508Shrs
125183840Srajstatic int mv_twsi_probe(device_t);
126183840Srajstatic int mv_twsi_attach(device_t);
127183840Srajstatic int mv_twsi_detach(device_t);
128183840Sraj
129183840Srajstatic int mv_twsi_reset(device_t dev, u_char speed, u_char addr,
130183840Sraj    u_char *oldaddr);
131183840Srajstatic int mv_twsi_repeated_start(device_t dev, u_char slave, int timeout);
132183840Srajstatic int mv_twsi_start(device_t dev, u_char slave, int timeout);
133183840Srajstatic int mv_twsi_stop(device_t dev);
134183840Srajstatic int mv_twsi_read(device_t dev, char *buf, int len, int *read, int last,
135183840Sraj    int delay);
136194015Savgstatic int mv_twsi_write(device_t dev, const char *buf, int len, int *sent,
137183840Sraj    int timeout);
138183840Sraj
139183840Srajstatic struct resource_spec res_spec[] = {
140183840Sraj	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
141183840Sraj	{ -1, 0 }
142183840Sraj};
143183840Sraj
144183840Srajstatic device_method_t mv_twsi_methods[] = {
145183840Sraj	/* device interface */
146183840Sraj	DEVMETHOD(device_probe,		mv_twsi_probe),
147183840Sraj	DEVMETHOD(device_attach,	mv_twsi_attach),
148183840Sraj	DEVMETHOD(device_detach,	mv_twsi_detach),
149183840Sraj
150183840Sraj	/* iicbus interface */
151183840Sraj	DEVMETHOD(iicbus_callback, iicbus_null_callback),
152183840Sraj	DEVMETHOD(iicbus_repeated_start, mv_twsi_repeated_start),
153183840Sraj	DEVMETHOD(iicbus_start,		mv_twsi_start),
154183840Sraj	DEVMETHOD(iicbus_stop,		mv_twsi_stop),
155183840Sraj	DEVMETHOD(iicbus_write,		mv_twsi_write),
156183840Sraj	DEVMETHOD(iicbus_read,		mv_twsi_read),
157183840Sraj	DEVMETHOD(iicbus_reset,		mv_twsi_reset),
158183840Sraj	DEVMETHOD(iicbus_transfer,	iicbus_transfer_gen),
159183840Sraj	{ 0, 0 }
160183840Sraj};
161183840Sraj
162183840Srajstatic devclass_t mv_twsi_devclass;
163183840Sraj
164183840Srajstatic driver_t mv_twsi_driver = {
165183840Sraj	MV_TWSI_NAME,
166183840Sraj	mv_twsi_methods,
167183840Sraj	sizeof(struct mv_twsi_softc),
168183840Sraj};
169183840Sraj
170209131SrajDRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0);
171183840SrajDRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0);
172183840SrajMODULE_DEPEND(twsi, iicbus, 1, 1, 1);
173183840Sraj
174183840Srajstatic __inline uint32_t
175183840SrajTWSI_READ(struct mv_twsi_softc *sc, bus_size_t off)
176183840Sraj{
177183840Sraj
178183840Sraj	return (bus_read_4(sc->res[0], off));
179183840Sraj}
180183840Sraj
181183840Srajstatic __inline void
182183840SrajTWSI_WRITE(struct mv_twsi_softc *sc, bus_size_t off, uint32_t val)
183183840Sraj{
184183840Sraj
185183840Sraj	bus_write_4(sc->res[0], off, val);
186183840Sraj}
187183840Sraj
188183840Srajstatic __inline void
189183840Srajtwsi_control_clear(struct mv_twsi_softc *sc, uint32_t mask)
190183840Sraj{
191183840Sraj	uint32_t val;
192183840Sraj
193183840Sraj	val = TWSI_READ(sc, TWSI_CONTROL);
194183840Sraj	val &= ~mask;
195183840Sraj	TWSI_WRITE(sc, TWSI_CONTROL, val);
196183840Sraj}
197183840Sraj
198183840Srajstatic __inline void
199183840Srajtwsi_control_set(struct mv_twsi_softc *sc, uint32_t mask)
200183840Sraj{
201183840Sraj	uint32_t val;
202183840Sraj
203183840Sraj	val = TWSI_READ(sc, TWSI_CONTROL);
204183840Sraj	val |= mask;
205183840Sraj	TWSI_WRITE(sc, TWSI_CONTROL, val);
206183840Sraj}
207183840Sraj
208183840Srajstatic __inline void
209183840Srajtwsi_clear_iflg(struct mv_twsi_softc *sc)
210183840Sraj{
211183840Sraj
212183840Sraj	DELAY(1000);
213183840Sraj	twsi_control_clear(sc, TWSI_CONTROL_IFLG);
214183840Sraj	DELAY(1000);
215183840Sraj}
216183840Sraj
217183840Sraj
218183840Sraj/*
219183840Sraj * timeout given in us
220183840Sraj * returns
221183840Sraj *   0 on sucessfull mask change
222183840Sraj *   non-zero on timeout
223183840Sraj */
224183840Srajstatic int
225183840Srajtwsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask)
226183840Sraj{
227183840Sraj
228183840Sraj	timeout /= 10;
229183840Sraj	while (!(TWSI_READ(sc, TWSI_CONTROL) & mask)) {
230183840Sraj		DELAY(10);
231183840Sraj		if (--timeout < 0)
232183840Sraj			return (timeout);
233183840Sraj	}
234183840Sraj	return (0);
235183840Sraj}
236183840Sraj
237183840Sraj
238183840Sraj/*
239183840Sraj * 'timeout' is given in us. Note also that timeout handling is not exact --
240183840Sraj * twsi_locked_start() total wait can be more than 2 x timeout
241183840Sraj * (twsi_poll_ctrl() is called twice). 'mask' can be either TWSI_STATUS_START
242183840Sraj * or TWSI_STATUS_RPTD_START
243183840Sraj */
244183840Srajstatic int
245183840Srajtwsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask,
246183840Sraj    u_char slave, int timeout)
247183840Sraj{
248183840Sraj	int read_access, iflg_set = 0;
249183840Sraj	uint32_t status;
250183840Sraj
251183840Sraj	mtx_assert(&sc->mutex, MA_OWNED);
252183840Sraj
253183840Sraj	if (mask == TWSI_STATUS_RPTD_START)
254183840Sraj		/* read IFLG to know if it should be cleared later; from NBSD */
255183840Sraj		iflg_set = TWSI_READ(sc, TWSI_CONTROL) & TWSI_CONTROL_IFLG;
256183840Sraj
257183840Sraj	twsi_control_set(sc, TWSI_CONTROL_START);
258183840Sraj
259183840Sraj	if (mask == TWSI_STATUS_RPTD_START && iflg_set) {
260183840Sraj		debugf("IFLG set, clearing\n");
261183840Sraj		twsi_clear_iflg(sc);
262183840Sraj	}
263183840Sraj
264183840Sraj	/*
265183840Sraj	 * Without this delay we timeout checking IFLG if the timeout is 0.
266183840Sraj	 * NBSD driver always waits here too.
267183840Sraj	 */
268183840Sraj	DELAY(1000);
269183840Sraj
270183840Sraj	if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) {
271183840Sraj		debugf("timeout sending %sSTART condition\n",
272183840Sraj		    mask == TWSI_STATUS_START ? "" : "repeated ");
273183840Sraj		return (IIC_ETIMEOUT);
274183840Sraj	}
275183840Sraj
276183840Sraj	status = TWSI_READ(sc, TWSI_STATUS);
277183840Sraj	if (status != mask) {
278183840Sraj		debugf("wrong status (%02x) after sending %sSTART condition\n",
279183840Sraj		    status, mask == TWSI_STATUS_START ? "" : "repeated ");
280183840Sraj		return (IIC_ESTATUS);
281183840Sraj	}
282183840Sraj
283183840Sraj	TWSI_WRITE(sc, TWSI_DATA, slave);
284183840Sraj	DELAY(1000);
285183840Sraj	twsi_clear_iflg(sc);
286183840Sraj
287183840Sraj	if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) {
288183840Sraj		debugf("timeout sending slave address\n");
289183840Sraj		return (IIC_ETIMEOUT);
290183840Sraj	}
291183840Sraj
292183840Sraj	read_access = (slave & 0x1) ? 1 : 0;
293183840Sraj	status = TWSI_READ(sc, TWSI_STATUS);
294183840Sraj	if (status != (read_access ?
295183840Sraj	    TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) {
296183840Sraj		debugf("no ACK (status: %02x) after sending slave address\n",
297183840Sraj		    status);
298183840Sraj		return (IIC_ENOACK);
299183840Sraj	}
300183840Sraj
301183840Sraj	return (IIC_NOERR);
302183840Sraj}
303183840Sraj
304183840Srajstatic int
305183840Srajmv_twsi_probe(device_t dev)
306183840Sraj{
307183840Sraj
308266152Sian	if (!ofw_bus_status_okay(dev))
309266152Sian		return (ENXIO);
310266152Sian
311209131Sraj	if (!ofw_bus_is_compatible(dev, "mrvl,twsi"))
312209131Sraj		return (ENXIO);
313209131Sraj
314183840Sraj	device_set_desc(dev, "Marvell Integrated I2C Bus Controller");
315183840Sraj	return (BUS_PROBE_DEFAULT);
316183840Sraj}
317183840Sraj
318239508Shrs#define	ABSSUB(a,b)	(((a) > (b)) ? (a) - (b) : (b) - (a))
319239508Shrsstatic void
320239508Shrsmv_twsi_cal_baud_rate(const uint32_t target, struct mv_twsi_baud_rate *rate)
321239508Shrs{
322239508Shrs	uint32_t clk, cur, diff, diff0;
323239508Shrs	int m, n, m0, n0;
324239508Shrs
325239508Shrs	/* Calculate baud rate. */
326239508Shrs	m0 = n0 = 4;	/* Default values on reset */
327239508Shrs	diff0 = 0xffffffff;
328239508Shrs	clk = get_tclk();
329239508Shrs
330239508Shrs	for (n = 0; n < 8; n++) {
331239508Shrs		for (m = 0; m < 16; m++) {
332239508Shrs			cur = TWSI_BAUD_RATE_RAW(clk,m,n);
333239508Shrs			diff = ABSSUB(target, cur);
334239508Shrs			if (diff < diff0) {
335239508Shrs				m0 = m;
336239508Shrs				n0 = n;
337239508Shrs				diff0 = diff;
338239508Shrs			}
339239508Shrs		}
340239508Shrs	}
341239508Shrs	rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0);
342239508Shrs	rate->param = TWSI_BAUD_RATE_PARAM(m0, n0);
343239508Shrs	rate->m = m0;
344239508Shrs	rate->n = n0;
345239508Shrs}
346239508Shrs
347183840Srajstatic int
348183840Srajmv_twsi_attach(device_t dev)
349183840Sraj{
350183840Sraj	struct mv_twsi_softc *sc;
351239508Shrs	phandle_t child, iicbusnode;
352239508Shrs	device_t childdev;
353239508Shrs	struct iicbus_ivar *devi;
354239508Shrs	char dname[32];	/* 32 is taken from struct u_device */
355239508Shrs	uint32_t paddr;
356239508Shrs	int len, error;
357183840Sraj
358183840Sraj	sc = device_get_softc(dev);
359183840Sraj	sc->dev = dev;
360239508Shrs	bzero(baud_rate, sizeof(baud_rate));
361183840Sraj
362183840Sraj	mtx_init(&sc->mutex, device_get_nameunit(dev), MV_TWSI_NAME, MTX_DEF);
363183840Sraj
364183840Sraj	/* Allocate IO resources */
365183840Sraj	if (bus_alloc_resources(dev, res_spec, sc->res)) {
366183840Sraj		device_printf(dev, "could not allocate resources\n");
367183840Sraj		mv_twsi_detach(dev);
368183840Sraj		return (ENXIO);
369183840Sraj	}
370183840Sraj
371239508Shrs	mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &baud_rate[IIC_SLOW]);
372239508Shrs	mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &baud_rate[IIC_FAST]);
373239508Shrs	if (bootverbose)
374239508Shrs		device_printf(dev, "calculated baud rates are:\n"
375239508Shrs		    " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n"
376239508Shrs		    " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n",
377239508Shrs		    baud_rate[IIC_SLOW].raw / 1000,
378239508Shrs		    baud_rate[IIC_SLOW].m,
379239508Shrs		    baud_rate[IIC_SLOW].n,
380239508Shrs		    baud_rate[IIC_FAST].raw / 1000,
381239508Shrs		    baud_rate[IIC_FAST].m,
382239508Shrs		    baud_rate[IIC_FAST].n);
383239508Shrs
384239508Shrs	sc->iicbus = device_add_child(dev, IICBUS_DEVNAME, -1);
385183840Sraj	if (sc->iicbus == NULL) {
386183840Sraj		device_printf(dev, "could not add iicbus child\n");
387183840Sraj		mv_twsi_detach(dev);
388183840Sraj		return (ENXIO);
389183840Sraj	}
390239508Shrs	/* Attach iicbus. */
391239508Shrs	bus_generic_attach(dev);
392183840Sraj
393239508Shrs	iicbusnode = 0;
394239508Shrs	/* Find iicbus as the child devices in the device tree. */
395239508Shrs	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
396239508Shrs	    child = OF_peer(child)) {
397239508Shrs		len = OF_getproplen(child, "model");
398239508Shrs		if (len <= 0 || len > sizeof(dname) - 1)
399239508Shrs			continue;
400239508Shrs		error = OF_getprop(child, "model", &dname, len);
401239508Shrs		dname[len + 1] = '\0';
402239508Shrs		if (error == -1)
403239508Shrs			continue;
404239508Shrs		len = strlen(dname);
405239508Shrs		if (len == strlen(IICBUS_DEVNAME) &&
406239508Shrs		    strncasecmp(dname, IICBUS_DEVNAME, len) == 0) {
407239508Shrs			iicbusnode = child;
408239508Shrs			break;
409239508Shrs		}
410239508Shrs	}
411239508Shrs	if (iicbusnode == 0)
412239508Shrs		goto attach_end;
413239508Shrs
414239508Shrs	/* Attach child devices onto iicbus. */
415239508Shrs	for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) {
416239508Shrs		/* Get slave address. */
417239508Shrs		error = OF_getprop(child, "i2c-address", &paddr, sizeof(paddr));
418239508Shrs		if (error == -1)
419239508Shrs			error = OF_getprop(child, "reg", &paddr, sizeof(paddr));
420239508Shrs		if (error == -1)
421239508Shrs			continue;
422239508Shrs
423239508Shrs		/* Get device driver name. */
424239508Shrs		len = OF_getproplen(child, "model");
425239508Shrs		if (len <= 0 || len > sizeof(dname) - 1)
426239508Shrs			continue;
427239508Shrs		OF_getprop(child, "model", &dname, len);
428239508Shrs		dname[len + 1] = '\0';
429239508Shrs
430239508Shrs		if (bootverbose)
431239508Shrs			device_printf(dev, "adding a device %s at %d.\n",
432239508Shrs			    dname, fdt32_to_cpu(paddr));
433239508Shrs		childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1);
434239508Shrs		devi = IICBUS_IVAR(childdev);
435239508Shrs		devi->addr = fdt32_to_cpu(paddr);
436239508Shrs	}
437239508Shrs
438239508Shrsattach_end:
439239508Shrs	bus_generic_attach(sc->iicbus);
440239508Shrs
441183840Sraj	return (0);
442183840Sraj}
443183840Sraj
444183840Srajstatic int
445183840Srajmv_twsi_detach(device_t dev)
446183840Sraj{
447183840Sraj	struct mv_twsi_softc *sc;
448183840Sraj	int rv;
449183840Sraj
450183840Sraj	sc = device_get_softc(dev);
451183840Sraj
452183840Sraj	if ((rv = bus_generic_detach(dev)) != 0)
453183840Sraj		return (rv);
454183840Sraj
455183840Sraj	if (sc->iicbus != NULL)
456183840Sraj		if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
457183840Sraj			return (rv);
458183840Sraj
459183840Sraj	bus_release_resources(dev, res_spec, sc->res);
460183840Sraj
461183840Sraj	mtx_destroy(&sc->mutex);
462183840Sraj	return (0);
463183840Sraj}
464183840Sraj
465183840Sraj/*
466183840Sraj * Only slave mode supported, disregard [old]addr
467183840Sraj */
468183840Srajstatic int
469183840Srajmv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
470183840Sraj{
471183840Sraj	struct mv_twsi_softc *sc;
472239508Shrs	uint32_t param;
473183840Sraj
474183840Sraj	sc = device_get_softc(dev);
475183840Sraj
476183840Sraj	switch (speed) {
477183840Sraj	case IIC_SLOW:
478183840Sraj	case IIC_FAST:
479239508Shrs		param = baud_rate[speed].param;
480183840Sraj		break;
481239508Shrs	case IIC_FASTEST:
482183840Sraj	case IIC_UNKNOWN:
483183840Sraj	default:
484239508Shrs		param = baud_rate[IIC_FAST].param;
485183840Sraj		break;
486183840Sraj	}
487183840Sraj
488183840Sraj	mtx_lock(&sc->mutex);
489183840Sraj	TWSI_WRITE(sc, TWSI_SOFT_RESET, 0x0);
490183840Sraj	DELAY(2000);
491239508Shrs	TWSI_WRITE(sc, TWSI_BAUD_RATE, param);
492183840Sraj	TWSI_WRITE(sc, TWSI_CONTROL, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK);
493183840Sraj	DELAY(1000);
494183840Sraj	mtx_unlock(&sc->mutex);
495183840Sraj
496183840Sraj	return (0);
497183840Sraj}
498183840Sraj
499183840Sraj/*
500183840Sraj * timeout is given in us
501183840Sraj */
502183840Srajstatic int
503183840Srajmv_twsi_repeated_start(device_t dev, u_char slave, int timeout)
504183840Sraj{
505183840Sraj	struct mv_twsi_softc *sc;
506183840Sraj	int rv;
507183840Sraj
508183840Sraj	sc = device_get_softc(dev);
509183840Sraj
510183840Sraj	mtx_lock(&sc->mutex);
511183840Sraj	rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave,
512183840Sraj	    timeout);
513183840Sraj	mtx_unlock(&sc->mutex);
514183840Sraj
515183840Sraj	if (rv) {
516183840Sraj		mv_twsi_stop(dev);
517183840Sraj		return (rv);
518183840Sraj	} else
519183840Sraj		return (IIC_NOERR);
520183840Sraj}
521183840Sraj
522183840Sraj/*
523183840Sraj * timeout is given in us
524183840Sraj */
525183840Srajstatic int
526183840Srajmv_twsi_start(device_t dev, u_char slave, int timeout)
527183840Sraj{
528183840Sraj	struct mv_twsi_softc *sc;
529183840Sraj	int rv;
530183840Sraj
531183840Sraj	sc = device_get_softc(dev);
532183840Sraj
533183840Sraj	mtx_lock(&sc->mutex);
534183840Sraj	rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout);
535183840Sraj	mtx_unlock(&sc->mutex);
536183840Sraj
537183840Sraj	if (rv) {
538183840Sraj		mv_twsi_stop(dev);
539183840Sraj		return (rv);
540183840Sraj	} else
541183840Sraj		return (IIC_NOERR);
542183840Sraj}
543183840Sraj
544183840Srajstatic int
545183840Srajmv_twsi_stop(device_t dev)
546183840Sraj{
547183840Sraj	struct mv_twsi_softc *sc;
548183840Sraj
549183840Sraj	sc = device_get_softc(dev);
550183840Sraj
551183840Sraj	mtx_lock(&sc->mutex);
552183840Sraj	twsi_control_set(sc, TWSI_CONTROL_STOP);
553183840Sraj	DELAY(1000);
554183840Sraj	twsi_clear_iflg(sc);
555183840Sraj	mtx_unlock(&sc->mutex);
556183840Sraj
557183840Sraj	return (IIC_NOERR);
558183840Sraj}
559183840Sraj
560183840Srajstatic int
561183840Srajmv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay)
562183840Sraj{
563183840Sraj	struct mv_twsi_softc *sc;
564183840Sraj	uint32_t status;
565183840Sraj	int last_byte, rv;
566183840Sraj
567183840Sraj	sc = device_get_softc(dev);
568183840Sraj
569183840Sraj	mtx_lock(&sc->mutex);
570183840Sraj	*read = 0;
571183840Sraj	while (*read < len) {
572183840Sraj		/*
573183840Sraj		 * Check if we are reading last byte of the last buffer,
574183840Sraj		 * do not send ACK then, per I2C specs
575183840Sraj		 */
576183840Sraj		last_byte = ((*read == len - 1) && last) ? 1 : 0;
577183840Sraj		if (last_byte)
578183840Sraj			twsi_control_clear(sc, TWSI_CONTROL_ACK);
579183840Sraj		else
580183840Sraj			twsi_control_set(sc, TWSI_CONTROL_ACK);
581183840Sraj
582183840Sraj		DELAY (1000);
583183840Sraj		twsi_clear_iflg(sc);
584183840Sraj
585183840Sraj		if (twsi_poll_ctrl(sc, delay, TWSI_CONTROL_IFLG)) {
586183840Sraj			debugf("timeout reading data\n");
587183840Sraj			rv = IIC_ETIMEOUT;
588183840Sraj			goto out;
589183840Sraj		}
590183840Sraj
591183840Sraj		status = TWSI_READ(sc, TWSI_STATUS);
592183840Sraj		if (status != (last_byte ?
593183840Sraj		    TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) {
594183840Sraj			debugf("wrong status (%02x) while reading\n", status);
595183840Sraj			rv = IIC_ESTATUS;
596183840Sraj			goto out;
597183840Sraj		}
598183840Sraj
599183840Sraj		*buf++ = TWSI_READ(sc, TWSI_DATA);
600183840Sraj		(*read)++;
601183840Sraj	}
602183840Sraj	rv = IIC_NOERR;
603183840Srajout:
604183840Sraj	mtx_unlock(&sc->mutex);
605183840Sraj	return (rv);
606183840Sraj}
607183840Sraj
608183840Srajstatic int
609194015Savgmv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout)
610183840Sraj{
611183840Sraj	struct mv_twsi_softc *sc;
612183840Sraj	uint32_t status;
613183840Sraj	int rv;
614183840Sraj
615183840Sraj	sc = device_get_softc(dev);
616183840Sraj
617183840Sraj	mtx_lock(&sc->mutex);
618183840Sraj	*sent = 0;
619183840Sraj	while (*sent < len) {
620183840Sraj		TWSI_WRITE(sc, TWSI_DATA, *buf++);
621183840Sraj
622183840Sraj		twsi_clear_iflg(sc);
623183840Sraj		if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) {
624183840Sraj			debugf("timeout writing data\n");
625183840Sraj			rv = IIC_ETIMEOUT;
626183840Sraj			goto out;
627183840Sraj		}
628183840Sraj
629183840Sraj		status = TWSI_READ(sc, TWSI_STATUS);
630183840Sraj		if (status != TWSI_STATUS_DATA_WR_ACK) {
631183840Sraj			debugf("wrong status (%02x) while writing\n", status);
632183840Sraj			rv = IIC_ESTATUS;
633183840Sraj			goto out;
634183840Sraj		}
635183840Sraj		(*sent)++;
636183840Sraj	}
637183840Sraj	rv = IIC_NOERR;
638183840Srajout:
639183840Sraj	mtx_unlock(&sc->mutex);
640183840Sraj	return (rv);
641183840Sraj}
642