Home | History | Annotate | Line # | Download | only in i2c
i2c_exec.c revision 1.11
      1 /*	$NetBSD: i2c_exec.c,v 1.11 2018/12/10 00:31:45 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2003 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *      This product includes software developed for the NetBSD Project by
     20  *      Wasabi Systems, Inc.
     21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 
     38 #include <sys/cdefs.h>
     39 __KERNEL_RCSID(0, "$NetBSD: i2c_exec.c,v 1.11 2018/12/10 00:31:45 thorpej Exp $");
     40 
     41 #include <sys/param.h>
     42 #include <sys/systm.h>
     43 #include <sys/device.h>
     44 #include <sys/module.h>
     45 #include <sys/event.h>
     46 #include <sys/conf.h>
     47 
     48 #define	_I2C_PRIVATE
     49 #include <dev/i2c/i2cvar.h>
     50 
     51 static uint8_t	iic_smbus_crc8(uint16_t);
     52 static uint8_t	iic_smbus_pec(int, uint8_t *, uint8_t *);
     53 
     54 static int	i2cexec_modcmd(modcmd_t, void *);
     55 
     56 /*
     57  * iic_acquire_bus:
     58  *
     59  *	Acquire the I2C bus for use by a client.
     60  */
     61 int
     62 iic_acquire_bus(i2c_tag_t tag, int flags)
     63 {
     64 
     65 	return (*tag->ic_acquire_bus)(tag->ic_cookie, flags);
     66 }
     67 
     68 /*
     69  * iic_release_bus:
     70  *
     71  *	Relese the I2C bus, allowing another client to use it.
     72  */
     73 void
     74 iic_release_bus(i2c_tag_t tag, int flags)
     75 {
     76 
     77 	(*tag->ic_release_bus)(tag->ic_cookie, flags);
     78 }
     79 
     80 /*
     81  * iic_exec:
     82  *
     83  *	Simplified I2C client interface engine.
     84  *
     85  *	This and the SMBus routines are the preferred interface for
     86  *	client access to I2C/SMBus, since many automated controllers
     87  *	do not provide access to the low-level primitives of the I2C
     88  *	bus protocol.
     89  */
     90 int
     91 iic_exec(i2c_tag_t tag, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
     92     size_t cmdlen, void *vbuf, size_t buflen, int flags)
     93 {
     94 	const uint8_t *cmd = vcmd;
     95 	uint8_t *buf = vbuf;
     96 	int error;
     97 	size_t len;
     98 
     99 	if ((flags & I2C_F_PEC) && cmdlen > 0 && tag->ic_exec != NULL) {
    100 		uint8_t data[33]; /* XXX */
    101 		uint8_t b[3];
    102 
    103 		b[0] = addr << 1;
    104 		b[1] = cmd[0];
    105 
    106 		switch (buflen) {
    107 		case 0:
    108 			data[0] = iic_smbus_pec(2, b, NULL);
    109 			buflen++;
    110 			break;
    111 		case 1:
    112 			b[2] = buf[0];
    113 			data[0] = iic_smbus_pec(3, b, NULL);
    114 			data[1] = b[2];
    115 			buflen++;
    116 			break;
    117 		case 2:
    118 			break;
    119 		default:
    120 			KASSERT(buflen+1 < sizeof(data));
    121 			memcpy(data, vbuf, buflen);
    122 			data[buflen] = iic_smbus_pec(2, b, data);
    123 			buflen++;
    124 			break;
    125 		}
    126 
    127 		return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd,
    128 					cmdlen, data, buflen, flags));
    129 	}
    130 
    131 	/*
    132 	 * Defer to the controller if it provides an exec function.  Use
    133 	 * it if it does.
    134 	 */
    135 	if (tag->ic_exec != NULL)
    136 		return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd,
    137 					cmdlen, buf, buflen, flags));
    138 
    139 	if ((len = cmdlen) != 0) {
    140 		if ((error = iic_initiate_xfer(tag, addr, flags)) != 0)
    141 			goto bad;
    142 		while (len--) {
    143 			if ((error = iic_write_byte(tag, *cmd++, flags)) != 0)
    144 				goto bad;
    145 		}
    146 	} else if (buflen == 0) {
    147 		/*
    148 		 * This is a quick_read()/quick_write() command with
    149 		 * neither command nor data bytes
    150 		 */
    151 		if (I2C_OP_STOP_P(op))
    152 			flags |= I2C_F_STOP;
    153 		if (I2C_OP_READ_P(op))
    154 			flags |= I2C_F_READ;
    155 		if ((error = iic_initiate_xfer(tag, addr, flags)) != 0)
    156 			goto bad;
    157 	}
    158 
    159 	if (I2C_OP_READ_P(op))
    160 		flags |= I2C_F_READ;
    161 
    162 	len = buflen;
    163 	while (len--) {
    164 		if (len == 0 && I2C_OP_STOP_P(op))
    165 			flags |= I2C_F_STOP;
    166 		if (I2C_OP_READ_P(op)) {
    167 			/* Send REPEATED START. */
    168 			if ((len + 1) == buflen &&
    169 			    (error = iic_initiate_xfer(tag, addr, flags)) != 0)
    170 				goto bad;
    171 			/* NACK on last byte. */
    172 			if (len == 0)
    173 				flags |= I2C_F_LAST;
    174 			if ((error = iic_read_byte(tag, buf++, flags)) != 0)
    175 				goto bad;
    176 		} else  {
    177 			/* Maybe send START. */
    178 			if ((len + 1) == buflen && cmdlen == 0 &&
    179 			    (error = iic_initiate_xfer(tag, addr, flags)) != 0)
    180 				goto bad;
    181 			if ((error = iic_write_byte(tag, *buf++, flags)) != 0)
    182 				goto bad;
    183 		}
    184 	}
    185 
    186 	return (0);
    187  bad:
    188 	iic_send_stop(tag, flags);
    189 	return (error);
    190 }
    191 
    192 /*
    193  * iic_smbus_write_byte:
    194  *
    195  *	Perform an SMBus "write byte" operation.
    196  */
    197 int
    198 iic_smbus_write_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    199     uint8_t val, int flags)
    200 {
    201 
    202 	return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1,
    203 			 &val, 1, flags));
    204 }
    205 
    206 /*
    207  * iic_smbus_write_word:
    208  *
    209  *	Perform an SMBus "write word" operation.
    210  */
    211 int
    212 iic_smbus_write_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    213     uint16_t val, int flags)
    214 {
    215 	uint8_t vbuf[2];
    216 
    217 	vbuf[0] = val & 0xff;
    218 	vbuf[1] = (val >> 8) & 0xff;
    219 
    220 	return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1,
    221 			 vbuf, 2, flags));
    222 }
    223 
    224 /*
    225  * iic_smbus_read_byte:
    226  *
    227  *	Perform an SMBus "read byte" operation.
    228  */
    229 int
    230 iic_smbus_read_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    231     uint8_t *valp, int flags)
    232 {
    233 
    234 	return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1,
    235 			 valp, 1, flags));
    236 }
    237 
    238 /*
    239  * iic_smbus_read_word:
    240  *
    241  *	Perform an SMBus "read word" operation.
    242  */
    243 int
    244 iic_smbus_read_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    245     uint16_t *valp, int flags)
    246 {
    247 
    248 	return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1,
    249 			 (uint8_t *)valp, 2, flags));
    250 }
    251 
    252 /*
    253  * iic_smbus_receive_byte:
    254  *
    255  *	Perform an SMBus "receive byte" operation.
    256  */
    257 int
    258 iic_smbus_receive_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t *valp,
    259     int flags)
    260 {
    261 
    262 	return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0,
    263 			 valp, 1, flags));
    264 }
    265 
    266 /*
    267  * iic_smbus_send_byte:
    268  *
    269  *	Perform an SMBus "send byte" operation.
    270  */
    271 int
    272 iic_smbus_send_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t val, int flags)
    273 {
    274 
    275 	return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0,
    276 			 &val, 1, flags));
    277 }
    278 
    279 /*
    280  * iic_smbus_quick_read:
    281  *
    282  *	Perform an SMBus "quick read" operation.
    283  */
    284 int
    285 iic_smbus_quick_read(i2c_tag_t tag, i2c_addr_t addr, int flags)
    286 {
    287 
    288 	return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0,
    289 			 NULL, 0, flags));
    290 }
    291 
    292 /*
    293  * iic_smbus_quick_write:
    294  *
    295  *	Perform an SMBus "quick write" operation.
    296  */
    297 int
    298 iic_smbus_quick_write(i2c_tag_t tag, i2c_addr_t addr, int flags)
    299 {
    300 
    301 	return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0,
    302 			 NULL, 0, flags));
    303 }
    304 
    305 /*
    306  * iic_smbus_block_read:
    307  *
    308  *	Perform an SMBus "block read" operation.
    309  */
    310 int
    311 iic_smbus_block_read(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    312     uint8_t *vbuf, size_t buflen, int flags)
    313 {
    314 
    315 	return (iic_exec(tag, I2C_OP_READ_BLOCK, addr, &cmd, 1,
    316 			 vbuf, buflen, flags));
    317 }
    318 
    319 /*
    320  * iic_smbus_block_write:
    321  *
    322  *	Perform an SMBus "block write" operation.
    323  */
    324 int
    325 iic_smbus_block_write(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
    326     uint8_t *vbuf, size_t buflen, int flags)
    327 {
    328 
    329 	return (iic_exec(tag, I2C_OP_WRITE_BLOCK, addr, &cmd, 1,
    330 			 vbuf, buflen, flags));
    331 }
    332 
    333 /*
    334  * iic_smbus_crc8
    335  *
    336  *	Private helper for calculating packet error checksum
    337  */
    338 static uint8_t
    339 iic_smbus_crc8(uint16_t data)
    340 {
    341 	int i;
    342 
    343 	for (i = 0; i < 8; i++) {
    344 		if (data & 0x8000)
    345 			data = data ^ (0x1070U << 3);
    346 		data = data << 1;
    347 	}
    348 
    349 	return (uint8_t)(data >> 8);
    350 }
    351 
    352 /*
    353  * iic_smbus_pec
    354  *
    355  *	Private function for calculating packet error checking on SMBus
    356  *	packets.
    357  */
    358 static uint8_t
    359 iic_smbus_pec(int count, uint8_t *s, uint8_t *r)
    360 {
    361 	int i;
    362 	uint8_t crc = 0;
    363 
    364 	for (i = 0; i < count; i++)
    365 		crc = iic_smbus_crc8((crc ^ s[i]) << 8);
    366 	if (r != NULL)
    367 		for (i = 0; i <= r[0]; i++)
    368 			crc = iic_smbus_crc8((crc ^ r[i]) << 8);
    369 
    370 	return crc;
    371 }
    372 
    373 MODULE(MODULE_CLASS_MISC, i2cexec, NULL);
    374 
    375 static int
    376 i2cexec_modcmd(modcmd_t cmd, void *opaque)
    377 {
    378 	switch (cmd) {
    379 	case MODULE_CMD_INIT:
    380 	case MODULE_CMD_FINI:
    381 		return 0;
    382 		break;
    383 	default:
    384 		return ENOTTY;
    385 	}
    386 }
    387