xref: /onnv-gate/usr/src/uts/sun4u/io/i2c/misc/i2c_svc.c (revision 7696:21f5c73c0c15)
10Sstevel@tonic-gate /*
2*7696SRichard.Bean@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3*7696SRichard.Bean@Sun.COM  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate #include <sys/types.h>
70Sstevel@tonic-gate #include <sys/conf.h>
80Sstevel@tonic-gate #include <sys/kmem.h>
90Sstevel@tonic-gate #include <sys/open.h>
100Sstevel@tonic-gate #include <sys/ddi.h>
110Sstevel@tonic-gate #include <sys/sunddi.h>
120Sstevel@tonic-gate #include <sys/file.h>
130Sstevel@tonic-gate #include <sys/modctl.h>
140Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h>
150Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc_impl.h>
160Sstevel@tonic-gate 
170Sstevel@tonic-gate kmutex_t i2c_svc_mutex;
180Sstevel@tonic-gate 
190Sstevel@tonic-gate static struct modldrv i2c_modldrv = {
200Sstevel@tonic-gate 	&mod_miscops,		/* type of module - misc */
21*7696SRichard.Bean@Sun.COM 	"I2C module",
220Sstevel@tonic-gate 	NULL,
230Sstevel@tonic-gate };
240Sstevel@tonic-gate 
250Sstevel@tonic-gate static struct modlinkage i2c_modlinkage = {
260Sstevel@tonic-gate 	MODREV_1,
270Sstevel@tonic-gate 	&i2c_modldrv,
280Sstevel@tonic-gate 	0
290Sstevel@tonic-gate };
300Sstevel@tonic-gate 
310Sstevel@tonic-gate i2c_nexus_reg_list_t *nexus_reg_list_head = NULL;
320Sstevel@tonic-gate 
330Sstevel@tonic-gate int i2csvcdebug = 0;
340Sstevel@tonic-gate 
350Sstevel@tonic-gate int
_init(void)360Sstevel@tonic-gate _init(void)
370Sstevel@tonic-gate {
380Sstevel@tonic-gate 	int error;
390Sstevel@tonic-gate 
400Sstevel@tonic-gate 	if ((error = mod_install(&i2c_modlinkage)) == 0) {
410Sstevel@tonic-gate 		mutex_init(&i2c_svc_mutex, NULL, MUTEX_DRIVER, NULL);
420Sstevel@tonic-gate 	}
430Sstevel@tonic-gate 
440Sstevel@tonic-gate 	return (error);
450Sstevel@tonic-gate }
460Sstevel@tonic-gate 
470Sstevel@tonic-gate int
_fini(void)480Sstevel@tonic-gate _fini(void)
490Sstevel@tonic-gate {
500Sstevel@tonic-gate 	int error;
510Sstevel@tonic-gate 
520Sstevel@tonic-gate 	if ((error = mod_remove(&i2c_modlinkage)) == 0) {
530Sstevel@tonic-gate 		mutex_destroy(&i2c_svc_mutex);
540Sstevel@tonic-gate 	}
550Sstevel@tonic-gate 
560Sstevel@tonic-gate 	return (error);
570Sstevel@tonic-gate }
580Sstevel@tonic-gate 
590Sstevel@tonic-gate int
_info(struct modinfo * modinfop)600Sstevel@tonic-gate _info(struct modinfo *modinfop)
610Sstevel@tonic-gate {
620Sstevel@tonic-gate 	return (mod_info(&i2c_modlinkage, modinfop));
630Sstevel@tonic-gate }
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * i2c_client_register is called by I2C client drivers,
670Sstevel@tonic-gate  * typically in attach, but before starting any bus transfers.
680Sstevel@tonic-gate  *
690Sstevel@tonic-gate  * dip	   - the client device's dip.
700Sstevel@tonic-gate  * i2c_hdl - pointer to a handle returned on success.
710Sstevel@tonic-gate  *
720Sstevel@tonic-gate  */
730Sstevel@tonic-gate int
i2c_client_register(dev_info_t * dip,i2c_client_hdl_t * i2c_hdl)740Sstevel@tonic-gate i2c_client_register(dev_info_t *dip, i2c_client_hdl_t *i2c_hdl)
750Sstevel@tonic-gate {
760Sstevel@tonic-gate 	dev_info_t *pdip;
770Sstevel@tonic-gate 	i2c_client_hdl_t hdl;
780Sstevel@tonic-gate 	i2c_nexus_reg_list_t *reg_list;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	pdip = ddi_get_parent(dip);
810Sstevel@tonic-gate 
820Sstevel@tonic-gate 	mutex_enter(&i2c_svc_mutex);
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	reg_list = nexus_reg_list_head;
850Sstevel@tonic-gate 	/*
860Sstevel@tonic-gate 	 * search parent reg list to find dip's parent.
870Sstevel@tonic-gate 	 */
880Sstevel@tonic-gate 	for (; reg_list != NULL; reg_list = reg_list->next) {
890Sstevel@tonic-gate 		if (reg_list->dip == pdip) {
900Sstevel@tonic-gate 			break;
910Sstevel@tonic-gate 		}
920Sstevel@tonic-gate 	}
930Sstevel@tonic-gate 
940Sstevel@tonic-gate 	mutex_exit(&i2c_svc_mutex);
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	if (reg_list == NULL) {
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 		return (I2C_FAILURE);
990Sstevel@tonic-gate 	}
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	hdl = kmem_alloc(sizeof (struct i2c_client_hdl_impl), KM_SLEEP);
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	CHDL(hdl)->chdl_dip = dip;
1040Sstevel@tonic-gate 	CHDL(hdl)->chdl_nexus_reg = &reg_list->nexus_reg;
1050Sstevel@tonic-gate 	*i2c_hdl = hdl;
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	return (I2C_SUCCESS);
1080Sstevel@tonic-gate }
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate /*
1110Sstevel@tonic-gate  * i2c_client_unregister() is called by the I2C client driver
1120Sstevel@tonic-gate  * when it no longer wishes to transmit on the I2C bus, typically
1130Sstevel@tonic-gate  * during its detach routine.
1140Sstevel@tonic-gate  *
1150Sstevel@tonic-gate  * hdl - handle previously returned by i2c_client_register().
1160Sstevel@tonic-gate  *
1170Sstevel@tonic-gate  */
1180Sstevel@tonic-gate void
i2c_client_unregister(i2c_client_hdl_t hdl)1190Sstevel@tonic-gate i2c_client_unregister(i2c_client_hdl_t hdl)
1200Sstevel@tonic-gate {
1210Sstevel@tonic-gate 	kmem_free(hdl, sizeof (struct i2c_client_hdl_impl));
1220Sstevel@tonic-gate }
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate /*
1250Sstevel@tonic-gate  * i2c_transfer() is called by client drivers to handle
1260Sstevel@tonic-gate  * I2C data transfers.  It performs some basic sanity checking of
1270Sstevel@tonic-gate  * flags vs. i2c_len and i2c_wlen values, and then calls the
1280Sstevel@tonic-gate  * parent's i2c_transfer() function to handle the actual transfer.
1290Sstevel@tonic-gate  */
1300Sstevel@tonic-gate int
i2c_transfer(i2c_client_hdl_t hdl,i2c_transfer_t * i2c_tran)1310Sstevel@tonic-gate i2c_transfer(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran)
1320Sstevel@tonic-gate {
1330Sstevel@tonic-gate 	switch (i2c_tran->i2c_flags) {
1340Sstevel@tonic-gate 	case I2C_WR:
1350Sstevel@tonic-gate 		if (i2c_tran->i2c_wlen == 0) {
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 			return (EINVAL);
1380Sstevel@tonic-gate 		}
1390Sstevel@tonic-gate 		break;
1400Sstevel@tonic-gate 	case I2C_RD:
1410Sstevel@tonic-gate 		if (i2c_tran->i2c_rlen == 0) {
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 			return (EINVAL);
1440Sstevel@tonic-gate 		}
1450Sstevel@tonic-gate 		break;
1460Sstevel@tonic-gate 	case I2C_WR_RD:
1470Sstevel@tonic-gate 		if (i2c_tran->i2c_wlen == 0 || i2c_tran->i2c_rlen == 0) {
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 			return (EINVAL);
1500Sstevel@tonic-gate 		}
1510Sstevel@tonic-gate 		break;
1520Sstevel@tonic-gate 	default:
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 		return (EINVAL);
1550Sstevel@tonic-gate 	}
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	if (CHDL(hdl)->chdl_nexus_reg->i2c_nexus_transfer != NULL) {
1580Sstevel@tonic-gate 		(*CHDL(hdl)->chdl_nexus_reg->i2c_nexus_transfer)
1590Sstevel@tonic-gate 				(CHDL(hdl)->chdl_dip, i2c_tran);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 		return (i2c_tran->i2c_result);
1620Sstevel@tonic-gate 	} else {
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		return (ENOTSUP);
1650Sstevel@tonic-gate 	}
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate /*
1690Sstevel@tonic-gate  * i2c_transfer_alloc() allocates a i2c_transfer structure along
1700Sstevel@tonic-gate  * with read and write buffers of size rlen and wlen respectively.
1710Sstevel@tonic-gate  *
1720Sstevel@tonic-gate  * i2c_hdl - handle returned previously by i2c_client_register()
1730Sstevel@tonic-gate  * i2c - address of pointer to allocated buffer returned on success.
1740Sstevel@tonic-gate  * wlen - write size buffer to allocate.  May be 0.
1750Sstevel@tonic-gate  * rlen - read size buffer to allocate.  May be 0.
1760Sstevel@tonic-gate  * flags - I2C_SLEEP or I2C_NOSLEEP
1770Sstevel@tonic-gate  */
1780Sstevel@tonic-gate /*ARGSUSED*/
1790Sstevel@tonic-gate int
i2c_transfer_alloc(i2c_client_hdl_t hdl,i2c_transfer_t ** i2c,ushort_t wlen,ushort_t rlen,uint_t flags)1800Sstevel@tonic-gate i2c_transfer_alloc(i2c_client_hdl_t hdl,
1810Sstevel@tonic-gate 			i2c_transfer_t **i2c,
1820Sstevel@tonic-gate 			ushort_t wlen,
1830Sstevel@tonic-gate 			ushort_t rlen,
1840Sstevel@tonic-gate 			uint_t flags)
1850Sstevel@tonic-gate {
1860Sstevel@tonic-gate 	i2c_transfer_alloc_t *i2cw;
1870Sstevel@tonic-gate 	int sleep;
1880Sstevel@tonic-gate 	int size;
1890Sstevel@tonic-gate 
1901791Szk194757 	/*
1911791Szk194757 	 * set i2c to NULL in case the caller just checks i2c
1921791Szk194757 	 * to determine failures.
1931791Szk194757 	 */
1941791Szk194757 	*i2c = NULL;
1951791Szk194757 
1960Sstevel@tonic-gate 	if (flags & I2C_SLEEP) {
1970Sstevel@tonic-gate 		sleep = KM_SLEEP;
1980Sstevel@tonic-gate 	} else if (flags & I2C_NOSLEEP) {
1990Sstevel@tonic-gate 		sleep = KM_NOSLEEP;
2000Sstevel@tonic-gate 	} else {
2010Sstevel@tonic-gate 		sleep = KM_NOSLEEP;
2020Sstevel@tonic-gate 	}
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	size = sizeof (i2c_transfer_alloc_t) + rlen + wlen;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	if ((i2cw = kmem_zalloc(size, sleep)) == NULL) {
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 		return (I2C_FAILURE);
2090Sstevel@tonic-gate 	}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	i2cw->i2cw_size = size;
2120Sstevel@tonic-gate 	i2cw->i2cw_i2ct.i2c_wlen = wlen;
2130Sstevel@tonic-gate 	i2cw->i2cw_i2ct.i2c_rlen = rlen;
2140Sstevel@tonic-gate 	if (wlen != 0) {
2150Sstevel@tonic-gate 		i2cw->i2cw_i2ct.i2c_wbuf = (uchar_t *)i2cw +
2160Sstevel@tonic-gate 			sizeof (i2c_transfer_alloc_t);
2170Sstevel@tonic-gate 	}
2180Sstevel@tonic-gate 	if (rlen != 0) {
2190Sstevel@tonic-gate 		i2cw->i2cw_i2ct.i2c_rbuf = (uchar_t *)i2cw +
2200Sstevel@tonic-gate 			sizeof (i2c_transfer_alloc_t) + wlen;
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 	*i2c = (i2c_transfer_t *)i2cw;
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	return (I2C_SUCCESS);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate /*
2280Sstevel@tonic-gate  * i2c_transfer_free() is called to free a buffer previously
2290Sstevel@tonic-gate  * allocated by i2c_transfer_allocate().
2300Sstevel@tonic-gate  *
2310Sstevel@tonic-gate  * i2c_hdl - handle returned previously by i2c_client_register()
2320Sstevel@tonic-gate  * i2c - buffer previously allocated by i2c_transfer_allocate()
2330Sstevel@tonic-gate  */
2340Sstevel@tonic-gate /*ARGSUSED*/
2350Sstevel@tonic-gate void
i2c_transfer_free(i2c_client_hdl_t hdl,i2c_transfer_t * i2c_tran)2360Sstevel@tonic-gate i2c_transfer_free(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran)
2370Sstevel@tonic-gate {
2380Sstevel@tonic-gate 	i2c_transfer_alloc_t *i2cw = (i2c_transfer_alloc_t *)i2c_tran;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	kmem_free(i2cw, i2cw->i2cw_size);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate /*
2440Sstevel@tonic-gate  * i2c_nexus_register() is called by the nexus driver to inform
2450Sstevel@tonic-gate  * I2C services that it is ready to accept transactions, and
2460Sstevel@tonic-gate  * give the I2C services a vector of functions.
2470Sstevel@tonic-gate  *
2480Sstevel@tonic-gate  * dip - dip of the bus controller
2490Sstevel@tonic-gate  * nexus_reg - pointer to reg structure of vector functions
2500Sstevel@tonic-gate  */
2510Sstevel@tonic-gate void
i2c_nexus_register(dev_info_t * dip,i2c_nexus_reg_t * nexus_reg)2520Sstevel@tonic-gate i2c_nexus_register(dev_info_t *dip, i2c_nexus_reg_t *nexus_reg)
2530Sstevel@tonic-gate {
2540Sstevel@tonic-gate 	i2c_nexus_reg_list_t *nexus_reglist;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	nexus_reglist = kmem_alloc(sizeof (struct i2c_nexus_reg_list),
2570Sstevel@tonic-gate 		KM_SLEEP);
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	mutex_enter(&i2c_svc_mutex);
2600Sstevel@tonic-gate 	nexus_reglist->next = nexus_reg_list_head;
2610Sstevel@tonic-gate 	nexus_reg_list_head = nexus_reglist;
2620Sstevel@tonic-gate 	mutex_exit(&i2c_svc_mutex);
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	nexus_reglist->nexus_reg = *nexus_reg;
2650Sstevel@tonic-gate 	nexus_reglist->dip = dip;
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate /*
2690Sstevel@tonic-gate  * i2c_nexus_unregister() is called by the nexus driver when
2700Sstevel@tonic-gate  * it is no longer able to accept transactions for its I2C
2710Sstevel@tonic-gate  * children.
2720Sstevel@tonic-gate  *
2730Sstevel@tonic-gate  * dip - dev_info pointer passed to i2c_nexus_register().
2740Sstevel@tonic-gate  */
2750Sstevel@tonic-gate void
i2c_nexus_unregister(dev_info_t * dip)2760Sstevel@tonic-gate i2c_nexus_unregister(dev_info_t *dip)
2770Sstevel@tonic-gate {
2780Sstevel@tonic-gate 	i2c_nexus_reg_list_t **reg_list;
2790Sstevel@tonic-gate 	i2c_nexus_reg_list_t *save = NULL;
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	mutex_enter(&i2c_svc_mutex);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	reg_list = &nexus_reg_list_head;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	/*
2860Sstevel@tonic-gate 	 * reg_list is the address of the pointer to an element on
2870Sstevel@tonic-gate 	 * the reg list.  It starts out being the address of the
2880Sstevel@tonic-gate 	 * list head, but then is changed to the address of the
2890Sstevel@tonic-gate 	 * next pointer in a list element.  Once the element to
2900Sstevel@tonic-gate 	 * delete is found, then we change the pointer to the
2910Sstevel@tonic-gate 	 * address found in the next pointer of the element to
2920Sstevel@tonic-gate 	 * be deleted.
2930Sstevel@tonic-gate 	 */
2940Sstevel@tonic-gate 	for (; *reg_list != NULL; reg_list = &(*reg_list)->next) {
2950Sstevel@tonic-gate 		if ((*reg_list)->dip == dip) {
2960Sstevel@tonic-gate 			save = *reg_list;
2970Sstevel@tonic-gate 			/* prev next pointer adjusted to point */
2980Sstevel@tonic-gate 			*reg_list = (*reg_list)->next;
2990Sstevel@tonic-gate 			break;
3000Sstevel@tonic-gate 		}
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate 	mutex_exit(&i2c_svc_mutex);
3030Sstevel@tonic-gate 	if (save != NULL) {
3040Sstevel@tonic-gate 		kmem_free(save, sizeof (i2c_nexus_reg_list_t));
3050Sstevel@tonic-gate 	} else {
3060Sstevel@tonic-gate 		cmn_err(CE_WARN, "could not find nexus reg to free");
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate }
309