Home | History | Annotate | Line # | Download | only in usb
auvitek_i2c.c revision 1.3.36.1
      1 /* $NetBSD: auvitek_i2c.c,v 1.3.36.1 2017/01/07 08:56:41 pgoyette Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) 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.36.1 2017/01/07 08:56:41 pgoyette 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 
     59 static int	auvitek_i2c_acquire_bus(void *, int);
     60 static void	auvitek_i2c_release_bus(void *, int);
     61 static int	auvitek_i2c_exec(void *, i2c_op_t, i2c_addr_t,
     62 				 const void *, size_t, void *, size_t, int);
     63 
     64 static int	auvitek_i2c_read(struct auvitek_softc *, i2c_addr_t,
     65 				 uint8_t *, size_t);
     66 static int	auvitek_i2c_write(struct auvitek_softc *, i2c_addr_t,
     67 				  const uint8_t *, size_t);
     68 static bool	auvitek_i2c_wait(struct auvitek_softc *);
     69 static bool	auvitek_i2c_wait_rdack(struct auvitek_softc *);
     70 static bool	auvitek_i2c_wait_rddone(struct auvitek_softc *);
     71 static bool	auvitek_i2c_wait_wrdone(struct auvitek_softc *);
     72 
     73 int
     74 auvitek_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 
     87 int
     88 auvitek_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 
     98 void
     99 auvitek_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 
    115 void
    116 auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child)
    117 {
    118 	if (sc->sc_i2cdev == child)
    119 		sc->sc_i2cdev = NULL;
    120 }
    121 
    122 static int
    123 auvitek_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 
    137 static void
    138 auvitek_i2c_release_bus(void *opaque, int flags)
    139 {
    140 	struct auvitek_softc *sc = opaque;
    141 
    142 	mutex_exit(&sc->sc_i2c_lock);
    143 }
    144 
    145 static int
    146 auvitek_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 
    157 static int
    158 auvitek_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 
    196 static int
    197 auvitek_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 
    243 static bool
    244 auvitek_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 
    261 static bool
    262 auvitek_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 
    279 static bool
    280 auvitek_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 
    297 static bool
    298 auvitek_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