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 = ®_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