xref: /onnv-gate/usr/src/uts/sun4u/io/i2c/nexus/smbus.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53923Sarutz  * Common Development and Distribution License (the "License").
63923Sarutz  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * This is the nexus driver for SMBUS devices.  It mostly does not use
290Sstevel@tonic-gate  * the SMBUS protocol so that it fits better into the solaris i2c
300Sstevel@tonic-gate  * framework.
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/conf.h>
350Sstevel@tonic-gate #include <sys/file.h>
360Sstevel@tonic-gate #include <sys/open.h>
370Sstevel@tonic-gate #include <sys/ddi.h>
380Sstevel@tonic-gate #include <sys/sunddi.h>
390Sstevel@tonic-gate #include <sys/modctl.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <sys/kmem.h>
420Sstevel@tonic-gate #include <sys/archsystm.h>
430Sstevel@tonic-gate #include <sys/platform_module.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
460Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h>
470Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc_impl.h>
480Sstevel@tonic-gate #include <sys/i2c/nexus/smbus.h>
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate  * static function declarations
520Sstevel@tonic-gate  */
530Sstevel@tonic-gate static uint_t smbus_intr_cmn(smbus_t *smbus, char *src);
540Sstevel@tonic-gate static void smbus_intr_timeout(void *arg);
550Sstevel@tonic-gate static void smbus_resume(dev_info_t *dip);
560Sstevel@tonic-gate static void smbus_suspend(dev_info_t *dip);
570Sstevel@tonic-gate static int smbus_bus_ctl(dev_info_t *dip, dev_info_t *rdip,
580Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
590Sstevel@tonic-gate static  int smbus_acquire(smbus_t *, dev_info_t *dip,
600Sstevel@tonic-gate 	i2c_transfer_t *tp);
610Sstevel@tonic-gate static  void smbus_release(smbus_t *);
620Sstevel@tonic-gate static int smbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
630Sstevel@tonic-gate static int smbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
640Sstevel@tonic-gate static void smbus_free_regs(smbus_t *smbus);
650Sstevel@tonic-gate static int smbus_setup_regs(dev_info_t *dip, smbus_t *smbus);
660Sstevel@tonic-gate static void smbus_reportdev(dev_info_t *dip, dev_info_t *rdip);
670Sstevel@tonic-gate static void smbus_uninitchild(dev_info_t *cdip);
680Sstevel@tonic-gate static int smbus_initchild(dev_info_t *cdip);
690Sstevel@tonic-gate static int smbus_rd(smbus_t *smbus);
700Sstevel@tonic-gate static int smbus_wr(smbus_t *smbus);
710Sstevel@tonic-gate static void smbus_put(smbus_t *smbus, uint8_t reg, uint8_t data, uint8_t flags);
720Sstevel@tonic-gate static uint8_t smbus_get(smbus_t *smbus, uint8_t reg);
730Sstevel@tonic-gate static int smbus_dip_to_addr(dev_info_t *dip);
740Sstevel@tonic-gate static uint_t smbus_intr(caddr_t arg);
750Sstevel@tonic-gate static int smbus_switch(smbus_t *smbus);
760Sstevel@tonic-gate 
770Sstevel@tonic-gate static struct bus_ops smbus_busops = {
780Sstevel@tonic-gate 	BUSO_REV,
790Sstevel@tonic-gate 	nullbusmap,			/* bus_map */
800Sstevel@tonic-gate 	NULL,				/* bus_get_intrspec */
810Sstevel@tonic-gate 	NULL,				/* bus_add_intrspec */
820Sstevel@tonic-gate 	NULL,				/* bus_remove_intrspec */
830Sstevel@tonic-gate 	NULL,				/* bus_map_fault */
840Sstevel@tonic-gate 	ddi_no_dma_map,			/* bus_dma_map */
850Sstevel@tonic-gate 	ddi_no_dma_allochdl,		/* bus_dma_allochdl */
860Sstevel@tonic-gate 	ddi_no_dma_freehdl,		/* bus_dma_freehdl */
870Sstevel@tonic-gate 	ddi_no_dma_bindhdl,		/* bus_dma_bindhdl */
880Sstevel@tonic-gate 	ddi_no_dma_unbindhdl,		/* bus_unbindhdl */
890Sstevel@tonic-gate 	ddi_no_dma_flush,		/* bus_dma_flush */
900Sstevel@tonic-gate 	ddi_no_dma_win,			/* bus_dma_win */
910Sstevel@tonic-gate 	ddi_no_dma_mctl,		/* bus_dma_ctl */
920Sstevel@tonic-gate 	smbus_bus_ctl,			/* bus_ctl */
930Sstevel@tonic-gate 	ddi_bus_prop_op,		/* bus_prop_op */
940Sstevel@tonic-gate 	NULL,				/* bus_get_eventcookie */
950Sstevel@tonic-gate 	NULL,				/* bus_add_eventcall */
960Sstevel@tonic-gate 	NULL,				/* bus_remove_eventcall */
970Sstevel@tonic-gate 	NULL,				/* bus_post_event */
980Sstevel@tonic-gate 	0,				/* bus_intr_ctl 	*/
990Sstevel@tonic-gate 	0,				/* bus_config		*/
1000Sstevel@tonic-gate 	0,				/* bus_unconfig		*/
1010Sstevel@tonic-gate 	0,				/* bus_fm_init		*/
1020Sstevel@tonic-gate 	0,				/* bus_fm_fini		*/
1030Sstevel@tonic-gate 	0,				/* bus_fm_access_enter	*/
1040Sstevel@tonic-gate 	0,				/* bus_fm_access_exit	*/
1050Sstevel@tonic-gate 	0,				/* bus_power		*/
1060Sstevel@tonic-gate 	i_ddi_intr_ops			/* bus_intr_op		*/
1070Sstevel@tonic-gate };
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate struct cb_ops smbus_cb_ops = {
1100Sstevel@tonic-gate 	nodev,			/* open */
1110Sstevel@tonic-gate 	nodev,			/* close */
1120Sstevel@tonic-gate 	nodev,			/* strategy */
1130Sstevel@tonic-gate 	nodev,			/* print */
1140Sstevel@tonic-gate 	nodev,			/* dump */
1150Sstevel@tonic-gate 	nodev,			/* read */
1160Sstevel@tonic-gate 	nodev,			/* write */
1170Sstevel@tonic-gate 	nodev,			/* ioctl */
1180Sstevel@tonic-gate 	nodev,			/* devmap */
1190Sstevel@tonic-gate 	nodev,			/* mmap */
1200Sstevel@tonic-gate 	nodev,			/* segmap */
1210Sstevel@tonic-gate 	nochpoll,		/* poll */
1220Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
1230Sstevel@tonic-gate 	0,			/* streamtab  */
1240Sstevel@tonic-gate 	D_MP | D_NEW		/* Driver compatibility flag */
1250Sstevel@tonic-gate };
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate static struct dev_ops smbus_ops = {
1280Sstevel@tonic-gate 	DEVO_REV,
1290Sstevel@tonic-gate 	0,
1300Sstevel@tonic-gate 	ddi_no_info,
1310Sstevel@tonic-gate 	nulldev,
1320Sstevel@tonic-gate 	nulldev,
1330Sstevel@tonic-gate 	smbus_attach,
1340Sstevel@tonic-gate 	smbus_detach,
1350Sstevel@tonic-gate 	nodev,
1360Sstevel@tonic-gate 	&smbus_cb_ops,
137*7656SSherry.Moore@Sun.COM 	&smbus_busops,
138*7656SSherry.Moore@Sun.COM 	NULL,
139*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1400Sstevel@tonic-gate };
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate static struct modldrv modldrv = {
1430Sstevel@tonic-gate 	&mod_driverops, /* Type of module. This one is a driver */
144*7656SSherry.Moore@Sun.COM 	"SMBUS nexus Driver",	/* Name of the module. */
1450Sstevel@tonic-gate 	&smbus_ops,		/* driver ops */
1460Sstevel@tonic-gate };
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate static struct modlinkage modlinkage = {
1490Sstevel@tonic-gate 	MODREV_1,
1500Sstevel@tonic-gate 	&modldrv,
1510Sstevel@tonic-gate 	NULL
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate /*
1550Sstevel@tonic-gate  * Globals
1560Sstevel@tonic-gate  */
1570Sstevel@tonic-gate static void	*smbus_state;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate static int intr_timeout = INTR_TIMEOUT;
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate /*
1620Sstevel@tonic-gate  * The "interrupt-priorities" property is how a driver can specify a SPARC
1630Sstevel@tonic-gate  * PIL level to associate with each of its interrupt properties.  Most
1640Sstevel@tonic-gate  * self-identifying busses have a better mechanism for managing this, but I2C
1650Sstevel@tonic-gate  * doesn't.
1660Sstevel@tonic-gate  */
1670Sstevel@tonic-gate int smbus_pil = SMBUS_PIL;
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate i2c_nexus_reg_t smbus_regvec = {
1700Sstevel@tonic-gate 	I2C_NEXUS_REV,
1710Sstevel@tonic-gate 	smbus_transfer,
1720Sstevel@tonic-gate };
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate #ifdef DEBUG
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate static int smbus_print_lvl = 0;
1770Sstevel@tonic-gate static char msg_buff[1024];
1780Sstevel@tonic-gate static kmutex_t msg_buf_lock;
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate void
smbus_print(int flags,const char * fmt,...)1810Sstevel@tonic-gate smbus_print(int flags, const char *fmt, ...)
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate 	if (flags & smbus_print_lvl) {
1840Sstevel@tonic-gate 		va_list ap;
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 		va_start(ap, fmt);
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 		if (smbus_print_lvl & PRT_PROM) {
1890Sstevel@tonic-gate 			prom_vprintf(fmt, ap);
1900Sstevel@tonic-gate 		} else {
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 			mutex_enter(&msg_buf_lock);
1930Sstevel@tonic-gate 			(void) vsprintf(msg_buff, fmt, ap);
1940Sstevel@tonic-gate 			if (smbus_print_lvl & PRT_BUFFONLY) {
1950Sstevel@tonic-gate 				cmn_err(CE_CONT, "?%s", msg_buff);
1960Sstevel@tonic-gate 			} else {
1970Sstevel@tonic-gate 				cmn_err(CE_CONT, "%s", msg_buff);
1980Sstevel@tonic-gate 			}
1990Sstevel@tonic-gate 			mutex_exit(&msg_buf_lock);
2000Sstevel@tonic-gate 		}
2010Sstevel@tonic-gate 		va_end(ap);
2020Sstevel@tonic-gate 	}
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate #endif /* DEBUG */
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate int
_init(void)2070Sstevel@tonic-gate _init(void)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate 	int status;
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	status = ddi_soft_state_init(&smbus_state, sizeof (smbus_t),
212*7656SSherry.Moore@Sun.COM 	    1);
2130Sstevel@tonic-gate 	if (status != 0) {
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 		return (status);
2160Sstevel@tonic-gate 	}
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	if ((status = mod_install(&modlinkage)) != 0) {
2190Sstevel@tonic-gate 		ddi_soft_state_fini(&smbus_state);
2200Sstevel@tonic-gate 	} else {
2210Sstevel@tonic-gate #ifdef DEBUG
2220Sstevel@tonic-gate 		mutex_init(&msg_buf_lock, NULL, MUTEX_DRIVER, NULL);
2230Sstevel@tonic-gate #endif
2240Sstevel@tonic-gate 	}
2250Sstevel@tonic-gate 	return (status);
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate int
_fini(void)2290Sstevel@tonic-gate _fini(void)
2300Sstevel@tonic-gate {
2310Sstevel@tonic-gate 	int status;
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	if ((status = mod_remove(&modlinkage)) == 0) {
2340Sstevel@tonic-gate 		ddi_soft_state_fini(&smbus_state);
2350Sstevel@tonic-gate #ifdef DEBUG
2360Sstevel@tonic-gate 		mutex_destroy(&msg_buf_lock);
2370Sstevel@tonic-gate #endif
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	return (status);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate /*
2440Sstevel@tonic-gate  * The loadable-module _info(9E) entry point
2450Sstevel@tonic-gate  */
2460Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2470Sstevel@tonic-gate _info(struct modinfo *modinfop)
2480Sstevel@tonic-gate {
2490Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate static void
smbus_interrupts_on(smbus_t * smbus)2530Sstevel@tonic-gate smbus_interrupts_on(smbus_t *smbus)
2540Sstevel@tonic-gate {
2550Sstevel@tonic-gate 	int src_enable;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	src_enable = ddi_get32(smbus->smbus_confighandle,
2580Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2590Sstevel@tonic-gate 	src_enable |= SMBUS_SMI;
2600Sstevel@tonic-gate 	ddi_put32(smbus->smbus_confighandle,
2610Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA],
2620Sstevel@tonic-gate 	    src_enable);
2630Sstevel@tonic-gate 	(void) ddi_get32(smbus->smbus_confighandle,
2640Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate static void
smbus_interrupts_off(smbus_t * smbus)2680Sstevel@tonic-gate smbus_interrupts_off(smbus_t *smbus)
2690Sstevel@tonic-gate {
2700Sstevel@tonic-gate 	int src_enable;
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	src_enable = ddi_get32(smbus->smbus_confighandle,
2730Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2740Sstevel@tonic-gate 	src_enable &= ~SMBUS_SMI;
2750Sstevel@tonic-gate 	ddi_put32(smbus->smbus_confighandle,
2760Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA],
2770Sstevel@tonic-gate 	    src_enable);
2780Sstevel@tonic-gate 	(void) ddi_get32(smbus->smbus_confighandle,
2790Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate static void
smbus_dodetach(dev_info_t * dip)2830Sstevel@tonic-gate smbus_dodetach(dev_info_t *dip)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate 	smbus_t *smbus;
2860Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	smbus = ddi_get_soft_state(smbus_state, instance);
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	if (smbus == NULL) {
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 		return;
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	cv_destroy(&smbus->smbus_cv);
2960Sstevel@tonic-gate 	mutex_destroy(&smbus->smbus_mutex);
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	if ((smbus->smbus_attachflags & INTERRUPT_PRI) != 0) {
2990Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
3000Sstevel@tonic-gate 		    "interrupt-priorities");
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	smbus_free_regs(smbus);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	if ((smbus->smbus_attachflags & NEXUS_REGISTER) != 0) {
3060Sstevel@tonic-gate 		i2c_nexus_unregister(dip);
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate 	if ((smbus->smbus_attachflags & IMUTEX) != 0) {
3090Sstevel@tonic-gate 		mutex_destroy(&smbus->smbus_imutex);
3100Sstevel@tonic-gate 		cv_destroy(&smbus->smbus_icv);
3110Sstevel@tonic-gate 	}
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	if (smbus->smbus_timeout != 0) {
3140Sstevel@tonic-gate 		(void) untimeout(smbus->smbus_timeout);
3150Sstevel@tonic-gate 	}
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	if ((smbus->smbus_attachflags & ADD_INTR) != 0) {
3180Sstevel@tonic-gate 		ddi_remove_intr(dip, 0, smbus->smbus_icookie);
3190Sstevel@tonic-gate 	}
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	ddi_soft_state_free(smbus_state, instance);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate static int
smbus_doattach(dev_info_t * dip)3250Sstevel@tonic-gate smbus_doattach(dev_info_t *dip)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	smbus_t *smbus;
3280Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	/*
3310Sstevel@tonic-gate 	 * Allocate soft state structure.
3320Sstevel@tonic-gate 	 */
3330Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(smbus_state, instance) != DDI_SUCCESS) {
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 		goto bad;
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	smbus = ddi_get_soft_state(smbus_state, instance);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	(void) snprintf(smbus->smbus_name, sizeof (smbus->smbus_name),
3410Sstevel@tonic-gate 	    "%s%d", ddi_node_name(dip), instance);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	smbus->smbus_dip = dip;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	mutex_init(&smbus->smbus_mutex, NULL, MUTEX_DRIVER, NULL);
3460Sstevel@tonic-gate 	mutex_init(&smbus->smbus_imutex, NULL, MUTEX_DRIVER, NULL);
3470Sstevel@tonic-gate 	cv_init(&smbus->smbus_cv, NULL, CV_DRIVER, NULL);
3480Sstevel@tonic-gate 	cv_init(&smbus->smbus_intr_cv, NULL, CV_DRIVER, NULL);
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	if (smbus_setup_regs(dip, smbus) != DDI_SUCCESS) {
3510Sstevel@tonic-gate 		goto bad;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip,  DDI_PROP_DONTPASS,
3550Sstevel@tonic-gate 	    "interrupts") == 1) {
3560Sstevel@tonic-gate 		smbus->smbus_polling = 0;
3570Sstevel@tonic-gate 		/*
3580Sstevel@tonic-gate 		 * The "interrupt-priorities" property is how a driver can
3590Sstevel@tonic-gate 		 * specify a SPARC PIL level to associate with each of its
3600Sstevel@tonic-gate 		 * interrupt properties.  Most self-identifying busses have
3610Sstevel@tonic-gate 		 * a better mechanism for managing this, but I2C doesn't.
3620Sstevel@tonic-gate 		 */
3630Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
3640Sstevel@tonic-gate 		    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
3650Sstevel@tonic-gate 		    "interrupt-priorities") != 1) {
3660Sstevel@tonic-gate 			(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
3670Sstevel@tonic-gate 			    DDI_PROP_CANSLEEP, "interrupt-priorities",
3680Sstevel@tonic-gate 			    (caddr_t)&smbus_pil,
3690Sstevel@tonic-gate 			    sizeof (smbus_pil));
3700Sstevel@tonic-gate 			smbus->smbus_attachflags |= INTERRUPT_PRI;
3710Sstevel@tonic-gate 		}
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 		/*
3740Sstevel@tonic-gate 		 * Clear status to clear any possible interrupt
3750Sstevel@tonic-gate 		 */
3760Sstevel@tonic-gate 		smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 		if (ddi_get_iblock_cookie(dip, 0, &smbus->smbus_icookie) !=
3790Sstevel@tonic-gate 		    DDI_SUCCESS) {
3800Sstevel@tonic-gate 			goto bad;
3810Sstevel@tonic-gate 		}
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 		if (ddi_add_intr(dip, 0, NULL, NULL, smbus_intr,
3840Sstevel@tonic-gate 		    (caddr_t)smbus) != DDI_SUCCESS) {
3850Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s failed to add interrupt",
3860Sstevel@tonic-gate 			    smbus->smbus_name);
3870Sstevel@tonic-gate 			goto bad;
3880Sstevel@tonic-gate 		}
3890Sstevel@tonic-gate 		smbus->smbus_attachflags |= ADD_INTR;
3900Sstevel@tonic-gate 	} else {
3910Sstevel@tonic-gate 		smbus->smbus_polling = 1;
3920Sstevel@tonic-gate 		/* Clear status */
3930Sstevel@tonic-gate 		smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	/*
3970Sstevel@tonic-gate 	 * initialize a cv and mutex
3980Sstevel@tonic-gate 	 */
3990Sstevel@tonic-gate 	cv_init(&smbus->smbus_icv, NULL, CV_DRIVER, NULL);
4000Sstevel@tonic-gate 	mutex_init(&smbus->smbus_imutex, NULL, MUTEX_DRIVER,
4010Sstevel@tonic-gate 	    (void *)smbus->smbus_icookie);
4020Sstevel@tonic-gate 	smbus->smbus_attachflags |= IMUTEX;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	/*
4050Sstevel@tonic-gate 	 * Register with the i2c framework
4060Sstevel@tonic-gate 	 */
4070Sstevel@tonic-gate 	i2c_nexus_register(dip, &smbus_regvec);
4080Sstevel@tonic-gate 	smbus->smbus_attachflags |= NEXUS_REGISTER;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	return (DDI_SUCCESS);
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate bad:
4130Sstevel@tonic-gate 	smbus_dodetach(dip);
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	return (DDI_FAILURE);
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate static int
smbus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4190Sstevel@tonic-gate smbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4200Sstevel@tonic-gate {
4210Sstevel@tonic-gate 	switch (cmd) {
4220Sstevel@tonic-gate 	case DDI_ATTACH:
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 		return (smbus_doattach(dip));
4250Sstevel@tonic-gate 	case DDI_RESUME:
4260Sstevel@tonic-gate 		smbus_resume(dip);
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 		return (DDI_SUCCESS);
4290Sstevel@tonic-gate 	default:
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 		return (DDI_FAILURE);
4320Sstevel@tonic-gate 	}
4330Sstevel@tonic-gate }
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate static int
smbus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4360Sstevel@tonic-gate smbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4370Sstevel@tonic-gate {
4380Sstevel@tonic-gate 	switch (cmd) {
4390Sstevel@tonic-gate 	case DDI_DETACH:
4400Sstevel@tonic-gate 		smbus_dodetach(dip);
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 		return (DDI_SUCCESS);
4430Sstevel@tonic-gate 	case DDI_SUSPEND:
4440Sstevel@tonic-gate 		smbus_suspend(dip);
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 		return (DDI_SUCCESS);
4470Sstevel@tonic-gate 	default:
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 		return (DDI_FAILURE);
4500Sstevel@tonic-gate 	}
4510Sstevel@tonic-gate }
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate static int
smbus_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)4540Sstevel@tonic-gate smbus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
4550Sstevel@tonic-gate     void *arg, void *result)
4560Sstevel@tonic-gate {
4570Sstevel@tonic-gate 	switch (op) {
4580Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 		return (smbus_initchild((dev_info_t *)arg));
4610Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
4620Sstevel@tonic-gate 		smbus_uninitchild((dev_info_t *)arg);
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 		return (DDI_SUCCESS);
4653923Sarutz 	case DDI_CTLOPS_REPORTDEV:
4660Sstevel@tonic-gate 		smbus_reportdev(dip, rdip);
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 		return (DDI_SUCCESS);
4690Sstevel@tonic-gate 	case DDI_CTLOPS_DMAPMAPC:
4700Sstevel@tonic-gate 	case DDI_CTLOPS_POKE:
4710Sstevel@tonic-gate 	case DDI_CTLOPS_PEEK:
4720Sstevel@tonic-gate 	case DDI_CTLOPS_IOMIN:
4730Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTINT:
4740Sstevel@tonic-gate 	case DDI_CTLOPS_SIDDEV:
4750Sstevel@tonic-gate 	case DDI_CTLOPS_SLAVEONLY:
4760Sstevel@tonic-gate 	case DDI_CTLOPS_AFFINITY:
4770Sstevel@tonic-gate 	case DDI_CTLOPS_PTOB:
4780Sstevel@tonic-gate 	case DDI_CTLOPS_BTOP:
4790Sstevel@tonic-gate 	case DDI_CTLOPS_BTOPR:
4800Sstevel@tonic-gate 	case DDI_CTLOPS_DVMAPAGESIZE:
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 		return (DDI_FAILURE);
4830Sstevel@tonic-gate 	default:
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate static int
smbus_initchild(dev_info_t * cdip)4900Sstevel@tonic-gate smbus_initchild(dev_info_t *cdip)
4910Sstevel@tonic-gate {
4920Sstevel@tonic-gate 	int32_t cell_size;
4930Sstevel@tonic-gate 	int len;
4940Sstevel@tonic-gate 	int32_t regs[2];
4950Sstevel@tonic-gate 	int err;
4960Sstevel@tonic-gate 	smbus_ppvt_t *ppvt;
4970Sstevel@tonic-gate 	char name[30];
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	SMBUS_PRINT((PRT_INIT, "smbus_initchild ENTER: %s\n",
5000Sstevel@tonic-gate 	    ddi_node_name(cdip)));
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	len = sizeof (cell_size);
5030Sstevel@tonic-gate 	err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
504*7656SSherry.Moore@Sun.COM 	    DDI_PROP_CANSLEEP, "#address-cells",
505*7656SSherry.Moore@Sun.COM 	    (caddr_t)&cell_size, &len);
5060Sstevel@tonic-gate 	if (err != DDI_PROP_SUCCESS || len != sizeof (cell_size)) {
5070Sstevel@tonic-gate 		cmn_err(CE_WARN, "cannot find address-cells");
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 		return (DDI_FAILURE);
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	len = sizeof (regs);
5130Sstevel@tonic-gate 	err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
5140Sstevel@tonic-gate 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
5150Sstevel@tonic-gate 	    "reg", (caddr_t)regs, &len);
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	if (err != DDI_PROP_SUCCESS) {
5180Sstevel@tonic-gate 		cmn_err(CE_WARN, "cannot get reg property");
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 		return (DDI_FAILURE);
5210Sstevel@tonic-gate 	}
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	ppvt = kmem_zalloc(sizeof (smbus_ppvt_t), KM_SLEEP);
5240Sstevel@tonic-gate 	ddi_set_parent_data(cdip, ppvt);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	/*
5270Sstevel@tonic-gate 	 * The reg property contains an unused first element (which is
5280Sstevel@tonic-gate 	 * the mux addr on xcal), and the second element is the i2c bus
5290Sstevel@tonic-gate 	 * address of the device.
5300Sstevel@tonic-gate 	 */
5310Sstevel@tonic-gate 	ppvt->smbus_ppvt_addr = regs[1];
5320Sstevel@tonic-gate 	(void) sprintf(name, "%x", regs[1]);
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	ddi_set_name_addr(cdip, name);
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	SMBUS_PRINT((PRT_INIT, "smbus_initchild SUCCESS: %s\n",
5370Sstevel@tonic-gate 	    ddi_node_name(cdip)));
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	return (DDI_SUCCESS);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate static void
smbus_uninitchild(dev_info_t * cdip)5430Sstevel@tonic-gate smbus_uninitchild(dev_info_t *cdip)
5440Sstevel@tonic-gate {
5450Sstevel@tonic-gate 	smbus_ppvt_t *ppvt;
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	ppvt = ddi_get_parent_data(cdip);
5480Sstevel@tonic-gate 	ddi_set_parent_data(cdip, NULL);
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	ddi_set_name_addr(cdip, NULL);
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	kmem_free(ppvt, sizeof (smbus_ppvt_t));
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	SMBUS_PRINT((PRT_INIT, "smbus_uninitchild: %s\n", ddi_node_name(cdip)));
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate static void
smbus_reportdev(dev_info_t * dip,dev_info_t * rdip)5580Sstevel@tonic-gate smbus_reportdev(dev_info_t *dip, dev_info_t *rdip)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 	smbus_ppvt_t *ppvt;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	ppvt = ddi_get_parent_data(rdip);
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	cmn_err(CE_CONT, "?%s%d at %s%d: addr 0x%x",
5650Sstevel@tonic-gate 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
5660Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
5670Sstevel@tonic-gate 	    ppvt->smbus_ppvt_addr);
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate /*
5710Sstevel@tonic-gate  * smbus_setup_regs() is called to map in the registers
5720Sstevel@tonic-gate  * specific to the smbus.
5730Sstevel@tonic-gate  */
5740Sstevel@tonic-gate static int
smbus_setup_regs(dev_info_t * dip,smbus_t * smbus)5750Sstevel@tonic-gate smbus_setup_regs(dev_info_t *dip, smbus_t *smbus)
5760Sstevel@tonic-gate {
5770Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
5780Sstevel@tonic-gate 	int ret;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
5810Sstevel@tonic-gate 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
5820Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	ret = ddi_regs_map_setup(dip, 1, (caddr_t *)&smbus->smbus_regaddr,
5850Sstevel@tonic-gate 	    0, 0, &attr, &smbus->smbus_rhandle);
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	if (ret == DDI_FAILURE) {
5880Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s unable to map regs", smbus->smbus_name);
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	} else if (ret == DDI_REGS_ACC_CONFLICT) {
5910Sstevel@tonic-gate 		cmn_err(CE_WARN,
5920Sstevel@tonic-gate 		    "%s unable to map regs because of conflict",
5930Sstevel@tonic-gate 		    smbus->smbus_name);
5940Sstevel@tonic-gate 		ret = DDI_FAILURE;
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	if (ret == DDI_FAILURE) {
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 		return (ret);
6000Sstevel@tonic-gate 	}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	ret = ddi_regs_map_setup(dip, 0, (caddr_t *)&smbus->smbus_configregaddr,
6030Sstevel@tonic-gate 	    0, 0, &attr, &smbus->smbus_confighandle);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	if (ret == DDI_FAILURE) {
6060Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s unable to map config regs",
6070Sstevel@tonic-gate 		    smbus->smbus_name);
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	} else if (ret == DDI_REGS_ACC_CONFLICT) {
6100Sstevel@tonic-gate 		cmn_err(CE_WARN,
6110Sstevel@tonic-gate 		    "%s unable to map config regs because of conflict",
6120Sstevel@tonic-gate 		    smbus->smbus_name);
6130Sstevel@tonic-gate 		ret = DDI_FAILURE;
6140Sstevel@tonic-gate 	}
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	return (ret);
6170Sstevel@tonic-gate }
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate /*
6200Sstevel@tonic-gate  * smbus_free_regs() frees any registers previously allocated.
6210Sstevel@tonic-gate  */
6220Sstevel@tonic-gate static void
smbus_free_regs(smbus_t * smbus)6230Sstevel@tonic-gate smbus_free_regs(smbus_t *smbus)
6240Sstevel@tonic-gate {
6250Sstevel@tonic-gate 	if (smbus->smbus_regaddr != NULL) {
6260Sstevel@tonic-gate 		ddi_regs_map_free(&smbus->smbus_rhandle);
6270Sstevel@tonic-gate 	}
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	if (smbus->smbus_configregaddr != NULL) {
6300Sstevel@tonic-gate 		ddi_regs_map_free(&smbus->smbus_confighandle);
6310Sstevel@tonic-gate 	}
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate /*
6350Sstevel@tonic-gate  * smbus_dip_to_addr() takes a dip and returns an I2C address.
6360Sstevel@tonic-gate  */
6370Sstevel@tonic-gate static int
smbus_dip_to_addr(dev_info_t * cdip)6380Sstevel@tonic-gate smbus_dip_to_addr(dev_info_t *cdip)
6390Sstevel@tonic-gate {
6400Sstevel@tonic-gate 	smbus_ppvt_t *ppvt;
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	ppvt = ddi_get_parent_data(cdip);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	return (ppvt->smbus_ppvt_addr);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate /*
6480Sstevel@tonic-gate  * smbus_suspend() is called before the system suspends.  Existing
6490Sstevel@tonic-gate  * transfer in progress or waiting will complete, but new transfers are
6500Sstevel@tonic-gate  * effectively blocked by "acquiring" the bus.
6510Sstevel@tonic-gate  */
6520Sstevel@tonic-gate static void
smbus_suspend(dev_info_t * dip)6530Sstevel@tonic-gate smbus_suspend(dev_info_t *dip)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate 	smbus_t *smbus;
6560Sstevel@tonic-gate 	int instance;
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6590Sstevel@tonic-gate 	smbus = ddi_get_soft_state(smbus_state, instance);
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	(void) smbus_acquire(smbus, NULL, NULL);
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate /*
6650Sstevel@tonic-gate  * smbus_resume() is called when the system resumes from CPR.  It releases
6660Sstevel@tonic-gate  * the hold that was placed on the i2c bus, which allows any real
6670Sstevel@tonic-gate  * transfers to continue.
6680Sstevel@tonic-gate  */
6690Sstevel@tonic-gate static void
smbus_resume(dev_info_t * dip)6700Sstevel@tonic-gate smbus_resume(dev_info_t *dip)
6710Sstevel@tonic-gate {
6720Sstevel@tonic-gate 	smbus_t *smbus;
6730Sstevel@tonic-gate 	int instance;
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6760Sstevel@tonic-gate 	smbus = ddi_get_soft_state(smbus_state, instance);
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	smbus_release(smbus);
6790Sstevel@tonic-gate }
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate /*
6820Sstevel@tonic-gate  * smbus_acquire() is called by a thread wishing to "own" the SMbus.
6830Sstevel@tonic-gate  * It should not be held across multiple transfers.
6840Sstevel@tonic-gate  */
6850Sstevel@tonic-gate static int
smbus_acquire(smbus_t * smbus,dev_info_t * dip,i2c_transfer_t * tp)6860Sstevel@tonic-gate smbus_acquire(smbus_t *smbus, dev_info_t *dip, i2c_transfer_t *tp)
6870Sstevel@tonic-gate {
6880Sstevel@tonic-gate 	mutex_enter(&smbus->smbus_mutex);
6890Sstevel@tonic-gate 	while (smbus->smbus_busy) {
6900Sstevel@tonic-gate 		cv_wait(&smbus->smbus_cv, &smbus->smbus_mutex);
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate 	smbus->smbus_busy = 1;
6930Sstevel@tonic-gate 	mutex_exit(&smbus->smbus_mutex);
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 	/*
6960Sstevel@tonic-gate 	 * On systems where OBP shares a smbus controller with the
6970Sstevel@tonic-gate 	 * OS, plat_shared_i2c_enter will serialize access to the
6980Sstevel@tonic-gate 	 * smbus controller.  Do not grab this lock during CPR
6990Sstevel@tonic-gate 	 * suspend as the CPR thread also acquires this muxex
7000Sstevel@tonic-gate 	 * through through prom_setprop which causes recursive
7010Sstevel@tonic-gate 	 * mutex enter.
7020Sstevel@tonic-gate 	 *
7030Sstevel@tonic-gate 	 * dip == NULL during CPR.
7040Sstevel@tonic-gate 	 */
7050Sstevel@tonic-gate 	if ((&plat_shared_i2c_enter != NULL) && (dip != NULL)) {
7060Sstevel@tonic-gate 		plat_shared_i2c_enter(smbus->smbus_dip);
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	smbus->smbus_cur_tran = tp;
7100Sstevel@tonic-gate 	smbus->smbus_cur_dip = dip;
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 	return (SMBUS_SUCCESS);
7130Sstevel@tonic-gate }
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate /*
7160Sstevel@tonic-gate  * smbus_release() is called to release a hold made by smbus_acquire().
7170Sstevel@tonic-gate  */
7180Sstevel@tonic-gate static void
smbus_release(smbus_t * smbus)7190Sstevel@tonic-gate smbus_release(smbus_t *smbus)
7200Sstevel@tonic-gate {
7210Sstevel@tonic-gate 	mutex_enter(&smbus->smbus_mutex);
7220Sstevel@tonic-gate 	smbus->smbus_busy = 0;
7230Sstevel@tonic-gate 	cv_signal(&smbus->smbus_cv);
7240Sstevel@tonic-gate 	smbus->smbus_cur_tran = NULL;
7250Sstevel@tonic-gate 	smbus->smbus_cur_dip = NULL;
7260Sstevel@tonic-gate 	mutex_exit(&smbus->smbus_mutex);
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	if ((&plat_shared_i2c_exit != NULL) && (smbus->smbus_cur_dip != NULL)) {
7290Sstevel@tonic-gate 		plat_shared_i2c_exit(smbus->smbus_dip);
7300Sstevel@tonic-gate 	}
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate static void
smbus_put(smbus_t * smbus,uint8_t reg,uint8_t data,uint8_t flags)7340Sstevel@tonic-gate smbus_put(smbus_t *smbus, uint8_t reg, uint8_t data, uint8_t flags)
7350Sstevel@tonic-gate {
7360Sstevel@tonic-gate 	ddi_acc_handle_t hp = smbus->smbus_rhandle;
7370Sstevel@tonic-gate 	uint8_t *reg_addr = smbus->smbus_regaddr;
7380Sstevel@tonic-gate 	uint8_t *config_addr = smbus->smbus_configregaddr;
7390Sstevel@tonic-gate 	ddi_acc_handle_t config_handle = smbus->smbus_confighandle;
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	ddi_put8(hp, &reg_addr[reg], data);
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	SMBUS_PRINT((PRT_PUT, "smbus_put:  addr = %p data = %x\n",
7440Sstevel@tonic-gate 	    &reg_addr[reg], data));
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	/*
7470Sstevel@tonic-gate 	 * if FLUSH flag is passed, read a config regs to make sure
7480Sstevel@tonic-gate 	 * data written is flushed.
7490Sstevel@tonic-gate 	 */
7500Sstevel@tonic-gate 	if (flags & SMBUS_FLUSH) {
7510Sstevel@tonic-gate 		(void) ddi_get8(config_handle, &config_addr[0]);
7520Sstevel@tonic-gate 	}
7530Sstevel@tonic-gate }
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate static uint8_t
smbus_get(smbus_t * smbus,uint8_t reg)7560Sstevel@tonic-gate smbus_get(smbus_t *smbus, uint8_t reg)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	ddi_acc_handle_t hp = smbus->smbus_rhandle;
7600Sstevel@tonic-gate 	uint8_t *regaddr = smbus->smbus_regaddr;
7610Sstevel@tonic-gate 	uint8_t data;
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	data = ddi_get8(hp, &regaddr[reg]);
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 	SMBUS_PRINT((PRT_GET, "smbus_get: data = %x\n", data));
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	return (data);
7680Sstevel@tonic-gate }
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate /*
7720Sstevel@tonic-gate  * The southbridge smbus device appears to have a feature where
7730Sstevel@tonic-gate  * reads from the status register return 0 for a few microseconds
7740Sstevel@tonic-gate  * after clearing the status.
7750Sstevel@tonic-gate  *
7760Sstevel@tonic-gate  * "status_wait_idle" allows for this by retrying until
7770Sstevel@tonic-gate  * it gets the right answer or times out.  The loop count
7780Sstevel@tonic-gate  * and the delay are empirical. The routine uses up
7790Sstevel@tonic-gate  * 400 us if it fails.
7800Sstevel@tonic-gate  *
7810Sstevel@tonic-gate  * The fact that this routine waits for 10 us before the
7820Sstevel@tonic-gate  * first check is deliberate.
7830Sstevel@tonic-gate  */
7840Sstevel@tonic-gate static int
smbus_wait_idle(smbus_t * smbus)7850Sstevel@tonic-gate smbus_wait_idle(smbus_t *smbus)
7860Sstevel@tonic-gate {
7870Sstevel@tonic-gate 	int retries = 40;
7880Sstevel@tonic-gate 	int status;
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
7910Sstevel@tonic-gate 	do {
7920Sstevel@tonic-gate 		drv_usecwait(10);
7930Sstevel@tonic-gate 		status = smbus_get(smbus, SMB_STS);
7940Sstevel@tonic-gate 	} while (status != IDLE && --retries > 0);
7950Sstevel@tonic-gate 	return (status);
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate /*
7980Sstevel@tonic-gate  * smbus_transfer is the function that is registered with
7990Sstevel@tonic-gate  * I2C services to be called for each i2c transaction.
8000Sstevel@tonic-gate  */
8010Sstevel@tonic-gate int
smbus_transfer(dev_info_t * dip,i2c_transfer_t * tp)8020Sstevel@tonic-gate smbus_transfer(dev_info_t *dip, i2c_transfer_t *tp)
8030Sstevel@tonic-gate {
8040Sstevel@tonic-gate 	smbus_t *smbus;
8050Sstevel@tonic-gate 	uint8_t status;
8060Sstevel@tonic-gate 	clock_t ctime;
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	smbus = ddi_get_soft_state(smbus_state,
8090Sstevel@tonic-gate 	    ddi_get_instance(ddi_get_parent(dip)));
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	if (smbus_acquire(smbus, dip, tp) == SMBUS_FAILURE) {
8120Sstevel@tonic-gate 		tp->i2c_result = I2C_FAILURE;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 		return (I2C_FAILURE);
8150Sstevel@tonic-gate 	}
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	tp->i2c_r_resid = tp->i2c_rlen;
8180Sstevel@tonic-gate 	tp->i2c_w_resid = tp->i2c_wlen;
8190Sstevel@tonic-gate 	tp->i2c_result = I2C_SUCCESS;
8200Sstevel@tonic-gate 	smbus->smbus_retries = 0;
8210Sstevel@tonic-gate 	smbus->smbus_bytes_to_read = 0;
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	mutex_enter(&smbus->smbus_imutex);
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	SMBUS_PRINT((PRT_TRANS, "smbus_transfer: rlen=%d wlen=%d flags=%d",
8260Sstevel@tonic-gate 	    tp->i2c_r_resid, tp->i2c_w_resid, tp->i2c_flags));
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	/*
8290Sstevel@tonic-gate 	 * First clear the status bits, then read them back to determine
8300Sstevel@tonic-gate 	 * the current state.
8310Sstevel@tonic-gate 	 */
8320Sstevel@tonic-gate 	status = smbus_wait_idle(smbus);
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	if (status != IDLE) {
8350Sstevel@tonic-gate 		/*
8360Sstevel@tonic-gate 		 * Try to issue bus reset
8370Sstevel@tonic-gate 		 * First reset the state machine.
8380Sstevel@tonic-gate 		 */
8390Sstevel@tonic-gate 		smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH);
8400Sstevel@tonic-gate 		status = smbus_wait_idle(smbus);
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 		if (status != IDLE) {
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 			smbus_put(smbus, SMB_TYP, T_OUT, SMBUS_FLUSH);
8450Sstevel@tonic-gate 			status = smbus_wait_idle(smbus);
8460Sstevel@tonic-gate 			if (status != IDLE) {
8470Sstevel@tonic-gate 				cmn_err(CE_WARN,
8480Sstevel@tonic-gate 				    "%s smbus not idle.  Unable to reset %x",
8490Sstevel@tonic-gate 				    smbus->smbus_name, status);
8500Sstevel@tonic-gate 				smbus->smbus_cur_tran->i2c_result = I2C_FAILURE;
8510Sstevel@tonic-gate 				mutex_exit(&smbus->smbus_imutex);
8520Sstevel@tonic-gate 				smbus_release(smbus);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 				return (I2C_FAILURE);
8550Sstevel@tonic-gate 			} else {
8560Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s T_OUT reset required",
8570Sstevel@tonic-gate 				    smbus->smbus_name);
8580Sstevel@tonic-gate 			}
8590Sstevel@tonic-gate 		}
8600Sstevel@tonic-gate 	}
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	if (smbus_switch(smbus) != SMBUS_COMPLETE) {
8630Sstevel@tonic-gate 		if (smbus->smbus_polling) {
8640Sstevel@tonic-gate 			smbus->smbus_poll_complete = 0;
8650Sstevel@tonic-gate 			smbus->smbus_poll_retries = 0;
8660Sstevel@tonic-gate 			do {
8670Sstevel@tonic-gate 				drv_usecwait(SMBUS_POLL_INTERVAL);
8680Sstevel@tonic-gate 				(void) smbus_intr_cmn(smbus, SMBUS_POLL);
8690Sstevel@tonic-gate 			} while (!smbus->smbus_poll_complete);
8700Sstevel@tonic-gate 		} else {
8710Sstevel@tonic-gate 			/*
8720Sstevel@tonic-gate 			 * Start a timeout as there is a bug in southbridge
8730Sstevel@tonic-gate 			 * smbus where sometimes a transaction never starts,
8740Sstevel@tonic-gate 			 * and needs to be reinitiated.
8750Sstevel@tonic-gate 			 */
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 			smbus->smbus_timeout = timeout(smbus_intr_timeout,
8780Sstevel@tonic-gate 			    smbus, drv_usectohz(intr_timeout));
8790Sstevel@tonic-gate 			SMBUS_PRINT((PRT_TRANS,
8800Sstevel@tonic-gate 			    "starting timeout in smbus_transfer %p",
8810Sstevel@tonic-gate 			    smbus->smbus_timeout));
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 			ctime = ddi_get_lbolt();
8840Sstevel@tonic-gate 			ctime += drv_usectohz(SMBUS_TRANS_TIMEOUT);
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 			smbus_interrupts_on(smbus);
8870Sstevel@tonic-gate 
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 			cv_wait(&smbus->smbus_icv, &smbus->smbus_imutex);
8900Sstevel@tonic-gate 		}
8910Sstevel@tonic-gate 	}
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	mutex_exit(&smbus->smbus_imutex);
8950Sstevel@tonic-gate 	smbus_release(smbus);
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	return (tp->i2c_result);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate /*
9010Sstevel@tonic-gate  * This is called by smbus_intr_cmn() to figure out whether to call
9020Sstevel@tonic-gate  * smbus_wr or smbus_rd depending on the command and current state.
9030Sstevel@tonic-gate  */
9040Sstevel@tonic-gate static int
smbus_switch(smbus_t * smbus)9050Sstevel@tonic-gate smbus_switch(smbus_t *smbus)
9060Sstevel@tonic-gate {
9070Sstevel@tonic-gate 	int ret;
9080Sstevel@tonic-gate 	i2c_transfer_t *tp = smbus->smbus_cur_tran;
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	if (tp == NULL) {
9110Sstevel@tonic-gate 		cmn_err(CE_WARN,
9120Sstevel@tonic-gate 		    "%s smbus_cur_tran is NULL. Transaction failed",
9130Sstevel@tonic-gate 		    smbus->smbus_name);
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 		return (SMBUS_FAILURE);
9160Sstevel@tonic-gate 	}
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 	smbus->smbus_saved_w_resid = tp->i2c_w_resid;
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	switch (tp->i2c_flags) {
9210Sstevel@tonic-gate 	case I2C_WR:
9220Sstevel@tonic-gate 		ret = smbus_wr(smbus);
9230Sstevel@tonic-gate 		break;
9240Sstevel@tonic-gate 	case I2C_RD:
9250Sstevel@tonic-gate 		ret = smbus_rd(smbus);
9260Sstevel@tonic-gate 		break;
9270Sstevel@tonic-gate 	case I2C_WR_RD:
9280Sstevel@tonic-gate 		/*
9290Sstevel@tonic-gate 		 * We could do a bit more decoding here,
9300Sstevel@tonic-gate 		 * to allow the transactions that would
9310Sstevel@tonic-gate 		 * work as a single smbus command to
9320Sstevel@tonic-gate 		 * be done as such.  It's not really
9330Sstevel@tonic-gate 		 * worth the trouble.
9340Sstevel@tonic-gate 		 */
9350Sstevel@tonic-gate 		if (tp->i2c_w_resid > 0) {
9360Sstevel@tonic-gate 			ret = smbus_wr(smbus);
9370Sstevel@tonic-gate 		} else {
9380Sstevel@tonic-gate 			ret = smbus_rd(smbus);
9390Sstevel@tonic-gate 		}
9400Sstevel@tonic-gate 		break;
9410Sstevel@tonic-gate 	default:
9420Sstevel@tonic-gate 		tp->i2c_result = I2C_FAILURE;
9430Sstevel@tonic-gate 		ret = SMBUS_COMPLETE;
9440Sstevel@tonic-gate 		break;
9450Sstevel@tonic-gate 	}
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	return (ret);
9480Sstevel@tonic-gate }
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate /*
9510Sstevel@tonic-gate  *
9520Sstevel@tonic-gate  */
9530Sstevel@tonic-gate static void
smbus_intr_timeout(void * arg)9540Sstevel@tonic-gate smbus_intr_timeout(void *arg)
9550Sstevel@tonic-gate {
9560Sstevel@tonic-gate 	smbus_t *smbus = (smbus_t *)arg;
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	mutex_enter(&smbus->smbus_imutex);
9590Sstevel@tonic-gate 	/*
9600Sstevel@tonic-gate 	 * If timeout is already cleared, it means interrupt arrived
9610Sstevel@tonic-gate 	 * while timeout fired.  In this case, just return from here.
9620Sstevel@tonic-gate 	 */
9630Sstevel@tonic-gate 	if (smbus->smbus_timeout == 0) {
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 		mutex_exit(&smbus->smbus_imutex);
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 		return;
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	(void) smbus_intr_cmn(smbus, SMBUS_TIMEOUT);
9710Sstevel@tonic-gate 	mutex_exit(&smbus->smbus_imutex);
9720Sstevel@tonic-gate }
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate /*
9750Sstevel@tonic-gate  * smbus_intr() is the interrupt handler for smbus.
9760Sstevel@tonic-gate  */
9770Sstevel@tonic-gate static uint_t
smbus_intr(caddr_t arg)9780Sstevel@tonic-gate smbus_intr(caddr_t arg)
9790Sstevel@tonic-gate {
9800Sstevel@tonic-gate 	smbus_t *smbus = (smbus_t *)arg;
9810Sstevel@tonic-gate 	uint32_t intr_status;
9820Sstevel@tonic-gate 	uint_t result;
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 	/*
9850Sstevel@tonic-gate 	 * Check to see if intr is really from smbus
9860Sstevel@tonic-gate 	 */
9870Sstevel@tonic-gate 	intr_status = ddi_get32(smbus->smbus_confighandle,
9880Sstevel@tonic-gate 	    (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_STATUS]);
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	if ((intr_status & SMBUS_SMB_INTR_STATUS) == 0) {
9920Sstevel@tonic-gate 		SMBUS_PRINT((PRT_INTR, "smbus_intr: intr not from smbus\n"));
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
9950Sstevel@tonic-gate 	}
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	mutex_enter(&smbus->smbus_imutex);
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 	/*
10000Sstevel@tonic-gate 	 * If timeout is already cleared, it means it arrived before the intr.
10010Sstevel@tonic-gate 	 * In that case, just return from here.
10020Sstevel@tonic-gate 	 */
10030Sstevel@tonic-gate 	if (smbus->smbus_timeout == 0) {
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 		mutex_exit(&smbus->smbus_imutex);
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
10080Sstevel@tonic-gate 	}
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 	result = smbus_intr_cmn(smbus, SMBUS_INTR);
10110Sstevel@tonic-gate 	mutex_exit(&smbus->smbus_imutex);
10120Sstevel@tonic-gate 	return (result);
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate /*
10160Sstevel@tonic-gate  * smbus_intr() is the interrupt handler for smbus.
10170Sstevel@tonic-gate  */
10180Sstevel@tonic-gate static uint_t
smbus_intr_cmn(smbus_t * smbus,char * src)10190Sstevel@tonic-gate smbus_intr_cmn(smbus_t *smbus, char *src)
10200Sstevel@tonic-gate {
10210Sstevel@tonic-gate 	i2c_transfer_t *tp;
10220Sstevel@tonic-gate 	char error_str[128];
10230Sstevel@tonic-gate 	uint8_t status;
10240Sstevel@tonic-gate 	int ret = SMBUS_SUCCESS;
10250Sstevel@tonic-gate 	timeout_id_t timer_id;
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 	ASSERT(mutex_owned(&smbus->smbus_imutex));
10280Sstevel@tonic-gate 	error_str[0] = '\0';
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	smbus_interrupts_off(smbus);
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	tp = smbus->smbus_cur_tran;
10330Sstevel@tonic-gate 	/*
10340Sstevel@tonic-gate 	 * This only happens when top half is interrupted or
10350Sstevel@tonic-gate 	 * times out, then the interrupt arrives.  Interrupt
10360Sstevel@tonic-gate 	 * was already disabled by top half, so just exit.
10370Sstevel@tonic-gate 	 */
10380Sstevel@tonic-gate 	if (tp == NULL) {
10390Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
10400Sstevel@tonic-gate 	}
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	/*
10430Sstevel@tonic-gate 	 * This wait is required before reading the status, otherwise
10440Sstevel@tonic-gate 	 * a parity error can occur which causes a panic.  A bug with
10450Sstevel@tonic-gate 	 * southbridge SMBUS.
10460Sstevel@tonic-gate 	 */
10470Sstevel@tonic-gate 	drv_usecwait(15);
10480Sstevel@tonic-gate 	status = smbus_get(smbus, SMB_STS);
10490Sstevel@tonic-gate 	if (smbus->smbus_polling) {
10500Sstevel@tonic-gate 		/*
10510Sstevel@tonic-gate 		 * If we are polling, then we expect not to
10520Sstevel@tonic-gate 		 * get the right answer for a while,
10530Sstevel@tonic-gate 		 * so we don't go on to that error stuff
10540Sstevel@tonic-gate 		 * until we've polled the status for a
10550Sstevel@tonic-gate 		 * few times. We check for errors here to save time,
10560Sstevel@tonic-gate 		 * otherwise we would have to wait for the full
10570Sstevel@tonic-gate 		 * poll timeout before dealing with them.
10580Sstevel@tonic-gate 		 */
10590Sstevel@tonic-gate 		if (status != (CMD_CMPL|IDLE) &&
10600Sstevel@tonic-gate 		    (status & (FAILED|BUS_ERR|DRV_ERR)) == 0 &&
10610Sstevel@tonic-gate 		    smbus->smbus_poll_retries++ < SMBUS_POLL_MAX_RETRIES) {
10620Sstevel@tonic-gate 				return (DDI_INTR_CLAIMED);
10630Sstevel@tonic-gate 		}
10640Sstevel@tonic-gate 		/*
10650Sstevel@tonic-gate 		 * else either ...
10660Sstevel@tonic-gate 		 * [] the command has completed, or;
10670Sstevel@tonic-gate 		 * [] There has been an error, or;
10680Sstevel@tonic-gate 		 * [] we timed out waiting for something useful
10690Sstevel@tonic-gate 		 * to happen, so we go on to  to the error handling bit that
10700Sstevel@tonic-gate 		 * follows, * which will reset the controller then restart the
10710Sstevel@tonic-gate 		 * whole transaction.
10720Sstevel@tonic-gate 		 *
10730Sstevel@tonic-gate 		 * In all cases, clear "poll_retries" for the next command or
10740Sstevel@tonic-gate 		 * retry
10750Sstevel@tonic-gate 		 */
10760Sstevel@tonic-gate 		smbus->smbus_poll_retries = 0;
10770Sstevel@tonic-gate 	}
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	/*
10800Sstevel@tonic-gate 	 * A bug in southbridge SMBUS sometimes requires a reset.  Status
10810Sstevel@tonic-gate 	 * should NOT be IDLE without any other bit set.  If it is, the
10820Sstevel@tonic-gate 	 * transaction should be restarted.
10830Sstevel@tonic-gate 	 */
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	if (status == IDLE) {
10860Sstevel@tonic-gate 		(void) sprintf(error_str, "%s bus is idle, ", error_str);
10870Sstevel@tonic-gate 	}
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	if ((status & CMD_CMPL) == 0) {
10900Sstevel@tonic-gate 		(void) sprintf(error_str, "%s command failed to complete, ",
10910Sstevel@tonic-gate 		    error_str);
10920Sstevel@tonic-gate 	}
10930Sstevel@tonic-gate 	if (status & BUS_ERR) {
10940Sstevel@tonic-gate 		(void) sprintf(error_str, "%s bus error, ", error_str);
10950Sstevel@tonic-gate 	}
10960Sstevel@tonic-gate 	if (status & FAILED) {
10970Sstevel@tonic-gate 		(void) sprintf(error_str, "%s failed transaction, ", error_str);
10980Sstevel@tonic-gate 	}
10990Sstevel@tonic-gate 	if (status & DRV_ERR) {
11000Sstevel@tonic-gate 		(void) sprintf(error_str, "%s timeout or bus reset", error_str);
11010Sstevel@tonic-gate 	}
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	if (error_str[0] != '\0') {
11040Sstevel@tonic-gate 		(void) sprintf(error_str, "%s %s ", error_str, src);
11050Sstevel@tonic-gate 	}
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 	/*
11080Sstevel@tonic-gate 	 * Clear status to clear the interrupt.
11090Sstevel@tonic-gate 	 */
11100Sstevel@tonic-gate 	smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
11110Sstevel@tonic-gate 	if (error_str[0] != '\0') {
11120Sstevel@tonic-gate 		smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH);
11130Sstevel@tonic-gate 		if (smbus->smbus_retries++ < SMBUS_MAX_RETRIES) {
11140Sstevel@tonic-gate 			/*
11150Sstevel@tonic-gate 			 * XXXX There was a panic here when the
11160Sstevel@tonic-gate 			 * intr timeout was greater than the timeout
11170Sstevel@tonic-gate 			 * for the entire transfer.
11180Sstevel@tonic-gate 			 *
11190Sstevel@tonic-gate 			 * Restore the value of w_resid before the
11200Sstevel@tonic-gate 			 * last transaction.  r_resid doesn't need to
11210Sstevel@tonic-gate 			 * be restored because it is only decremented
11220Sstevel@tonic-gate 			 * after a successful read.  Need to do this
11230Sstevel@tonic-gate 			 * here since smbus_switch() keys off of a
11240Sstevel@tonic-gate 			 * resid to know whether to call smbus_rd() or
11250Sstevel@tonic-gate 			 * smbus_wr().
11260Sstevel@tonic-gate 			 */
11270Sstevel@tonic-gate 			tp->i2c_w_resid = smbus->smbus_saved_w_resid;
11280Sstevel@tonic-gate 			smbus->smbus_bytes_to_read = 0;
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 			SMBUS_PRINT((PRT_INTR_ERR,
11310Sstevel@tonic-gate 			    "retrying: %s %s w_resid=%d\n", error_str,
11320Sstevel@tonic-gate 			    src, tp->i2c_w_resid));
11330Sstevel@tonic-gate 		} else {
11340Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s max retries exceeded: %s",
11350Sstevel@tonic-gate 			    smbus->smbus_name, error_str);
11360Sstevel@tonic-gate 			/*
11370Sstevel@tonic-gate 			 * bailing, but first will reset the bus.
11380Sstevel@tonic-gate 			 */
11390Sstevel@tonic-gate 			smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH);
11400Sstevel@tonic-gate 			smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
11410Sstevel@tonic-gate 			smbus->smbus_cur_tran->i2c_result = I2C_FAILURE;
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate 			ret = SMBUS_FAILURE;
11440Sstevel@tonic-gate 		}
11450Sstevel@tonic-gate 	} else {
11460Sstevel@tonic-gate 		smbus->smbus_retries = 0;
11470Sstevel@tonic-gate 	}
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 	if (tp != NULL) {
11500Sstevel@tonic-gate 		SMBUS_PRINT((PRT_INTR, "flags=%d  wresid=%d r_resid=%d %s\n",
11510Sstevel@tonic-gate 		    tp->i2c_flags, tp->i2c_w_resid, tp->i2c_r_resid, src));
11520Sstevel@tonic-gate 	}
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate 	if (ret != SMBUS_FAILURE) {
11550Sstevel@tonic-gate 		ret = smbus_switch(smbus);
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	if (smbus->smbus_polling) {
11590Sstevel@tonic-gate 		if (ret == SMBUS_COMPLETE || ret == SMBUS_FAILURE) {
11600Sstevel@tonic-gate 			smbus->smbus_poll_complete = 1;
11610Sstevel@tonic-gate 		}
11620Sstevel@tonic-gate 	} else {
11630Sstevel@tonic-gate 		/*
11640Sstevel@tonic-gate 		 * Disable previous timeout.  In case it was about to fire this
11650Sstevel@tonic-gate 		 * will let it exit without doing anything.
11660Sstevel@tonic-gate 		 */
11670Sstevel@tonic-gate 		timer_id = smbus->smbus_timeout;
11680Sstevel@tonic-gate 		smbus->smbus_timeout = 0;
11690Sstevel@tonic-gate 		mutex_exit(&smbus->smbus_imutex);
11700Sstevel@tonic-gate 		(void) untimeout(timer_id);
11710Sstevel@tonic-gate 		mutex_enter(&smbus->smbus_imutex);
11720Sstevel@tonic-gate 		if (ret == SMBUS_COMPLETE || ret == SMBUS_FAILURE) {
11730Sstevel@tonic-gate 			cv_signal(&smbus->smbus_icv);
11740Sstevel@tonic-gate 		} else {
11750Sstevel@tonic-gate 			smbus_interrupts_on(smbus);
11760Sstevel@tonic-gate 			smbus->smbus_timeout = timeout(smbus_intr_timeout,
11770Sstevel@tonic-gate 			    smbus, drv_usectohz(intr_timeout));
11780Sstevel@tonic-gate 			SMBUS_PRINT((PRT_INTR, "smbus_intr starting timeout %p "
1179*7656SSherry.Moore@Sun.COM 			    "%s", smbus->smbus_timeout, src));
11800Sstevel@tonic-gate 		}
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
11840Sstevel@tonic-gate }
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate /*
11870Sstevel@tonic-gate  * smbus_wr handles writes to the smbus.  Unlike true I2C busses
11880Sstevel@tonic-gate  * such as provided by pcf8584, smbus attaches a start and stop bit for each
11890Sstevel@tonic-gate  * transaction, so this limits writes to the maximum number of bytes
11900Sstevel@tonic-gate  * in a single transaction, which is 33.
11910Sstevel@tonic-gate  *
11920Sstevel@tonic-gate  * If more than 33 bytes are contained in the transfer, a non-zero
11930Sstevel@tonic-gate  * residual has to be returned, and the calling driver has to restart
11940Sstevel@tonic-gate  * another transaction to complete writing out any remaining data.  The
11950Sstevel@tonic-gate  * reason for this is that most devices require a register/offset as the
11960Sstevel@tonic-gate  * first byte to be written for each SMBUS transaction.
11970Sstevel@tonic-gate  */
11980Sstevel@tonic-gate static int
smbus_wr(smbus_t * smbus)11990Sstevel@tonic-gate smbus_wr(smbus_t *smbus)
12000Sstevel@tonic-gate {
12010Sstevel@tonic-gate 	i2c_transfer_t *tp = smbus->smbus_cur_tran;
12020Sstevel@tonic-gate 	uint8_t addr = smbus_dip_to_addr(smbus->smbus_cur_dip);
12030Sstevel@tonic-gate 	int bytes_written = 0;
12040Sstevel@tonic-gate 	uint8_t a;
12050Sstevel@tonic-gate 	uint8_t b;
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	if (tp->i2c_w_resid != tp->i2c_wlen) {
12080Sstevel@tonic-gate 		return (SMBUS_COMPLETE);
12090Sstevel@tonic-gate 	}
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 	SMBUS_PRINT((PRT_WR, "smbus_wr:  addr = %x resid = %d\n",
12120Sstevel@tonic-gate 	    addr, tp->i2c_w_resid));
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	smbus_put(smbus, SMB_STS, 0xff, 0);
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate 	/*
12170Sstevel@tonic-gate 	 * Address must be re-written for each command and it has to
12180Sstevel@tonic-gate 	 * be written before SMB_TYP.
12190Sstevel@tonic-gate 	 */
12200Sstevel@tonic-gate 	smbus_put(smbus, DEV_ADDR, addr, 0);
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate 	switch (tp->i2c_w_resid) {
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 	case 1:
12250Sstevel@tonic-gate 		a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12260Sstevel@tonic-gate 		smbus_put(smbus, SMB_CMD, a, 0);
12270Sstevel@tonic-gate 		smbus_put(smbus, SMB_TYP, SEND_BYTE, 0);
12280Sstevel@tonic-gate 		SMBUS_PRINT((PRT_WR, "smbus_wr: send one byte:"
12290Sstevel@tonic-gate 		    " %d\n", a));
12300Sstevel@tonic-gate 		break;
12310Sstevel@tonic-gate 	case 2:
12320Sstevel@tonic-gate 		a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12330Sstevel@tonic-gate 		smbus_put(smbus, SMB_CMD, a, 0);
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 		b = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12360Sstevel@tonic-gate 		smbus_put(smbus, DEV_DATA0, b, 0);
12370Sstevel@tonic-gate 		smbus_put(smbus, SMB_TYP, WR_BYTE, 0);
12380Sstevel@tonic-gate 		SMBUS_PRINT((PRT_WR, "smbus_wr: send two bytes:"
12390Sstevel@tonic-gate 		    " %d %d\n", a, b));
12400Sstevel@tonic-gate 		break;
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	default:
12430Sstevel@tonic-gate 		/*
12440Sstevel@tonic-gate 		 * Write out as many bytes as possible in a single command.
12450Sstevel@tonic-gate 		 * Note that BLK_DATA just creats a byte stream.  ie, the
12460Sstevel@tonic-gate 		 * smbus protocol is not used or interpreted by this driver.
12470Sstevel@tonic-gate 		 */
12480Sstevel@tonic-gate 		smbus_put(smbus, SMB_TYP, WR_BLK, 0);
12490Sstevel@tonic-gate 		a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 		smbus_put(smbus, SMB_CMD, a, 0);
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 		SMBUS_PRINT((PRT_WR, "smbus_wr: send multiple bytes: "));
12540Sstevel@tonic-gate 		SMBUS_PRINT((PRT_WR, "%x ", a));
12550Sstevel@tonic-gate 
12560Sstevel@tonic-gate 		while (tp->i2c_w_resid != 0) {
12570Sstevel@tonic-gate 			a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12580Sstevel@tonic-gate 			smbus_put(smbus, BLK_DATA, a, 0);
12590Sstevel@tonic-gate 			SMBUS_PRINT((PRT_WR, "%x ", a));
12600Sstevel@tonic-gate 			/*
12610Sstevel@tonic-gate 			 * Note that MAX_BLK_SEND defines how many bytes may
12620Sstevel@tonic-gate 			 * be sent to the BLK_DATA register. The leading byte
12630Sstevel@tonic-gate 			 * already sent to the SMB_CMD register doesn't count
12640Sstevel@tonic-gate 			 * But ALL the BLK_DATA bytes count so pre-increment
12650Sstevel@tonic-gate 			 * bytes_written before testing.
12660Sstevel@tonic-gate 			 */
12670Sstevel@tonic-gate 			if (++bytes_written == MAX_BLK_SEND) {
12680Sstevel@tonic-gate 				break;
12690Sstevel@tonic-gate 			}
12700Sstevel@tonic-gate 		}
12710Sstevel@tonic-gate 		SMBUS_PRINT((PRT_WR, "\n"));
12720Sstevel@tonic-gate 		smbus_put(smbus, DEV_DATA0, bytes_written, 0);
12730Sstevel@tonic-gate 		break;
12740Sstevel@tonic-gate 	}
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	/*
12770Sstevel@tonic-gate 	 * writing anything to port reg starts transfer
12780Sstevel@tonic-gate 	 */
12790Sstevel@tonic-gate 	smbus_put(smbus, STR_PORT, 0, SMBUS_FLUSH);
12800Sstevel@tonic-gate 
12810Sstevel@tonic-gate 	return (SMBUS_PENDING);
12820Sstevel@tonic-gate }
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate /*
12850Sstevel@tonic-gate  * smbus_rd handles reads to the smbus.  Unlike a true I2C bus
12860Sstevel@tonic-gate  * such as provided by pcf8584, smbus attaches a start and stop bit
12870Sstevel@tonic-gate  * for each transaction, which limits reads to the maximum number of
12880Sstevel@tonic-gate  * bytes in a single SMBUS transaction.  (Block reads don't
12890Sstevel@tonic-gate  * seem to work on smbus, and the southbridge documentation is poor).
12900Sstevel@tonic-gate  *
12910Sstevel@tonic-gate  * It doesn't appear that reads spanning multiple I2C transactions
12920Sstevel@tonic-gate  * (ie each with a start-stop) affects the transfer when reading
12930Sstevel@tonic-gate  * multiple bytes from devices with internal counters.  The counter
12940Sstevel@tonic-gate  * is correctly maintained.
12950Sstevel@tonic-gate  *
12960Sstevel@tonic-gate  * RD_WORD and RD_BYTE write out the byte in the SMB_CMD register
12970Sstevel@tonic-gate  * before reading, so RCV_BYTE is used instead.
12980Sstevel@tonic-gate  *
12990Sstevel@tonic-gate  * Multi-byte reads iniatiate a SMBUS transaction for each byte to be
13000Sstevel@tonic-gate  * received.  Because register/offset information doesn't need to
13010Sstevel@tonic-gate  * be resent for each I2C transaction (as opposed to when writing data),
13020Sstevel@tonic-gate  * the driver can continue reading data in separate SMBUS transactions
13030Sstevel@tonic-gate  * until the requested buffer is filled.
13040Sstevel@tonic-gate  */
13050Sstevel@tonic-gate static int
smbus_rd(smbus_t * smbus)13060Sstevel@tonic-gate smbus_rd(smbus_t *smbus)
13070Sstevel@tonic-gate {
13080Sstevel@tonic-gate 	i2c_transfer_t *tp = smbus->smbus_cur_tran;
13090Sstevel@tonic-gate 	uint8_t addr = smbus_dip_to_addr(smbus->smbus_cur_dip);
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 	if (smbus->smbus_bytes_to_read == 1) {
13120Sstevel@tonic-gate 		tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] =
13130Sstevel@tonic-gate 		    smbus_get(smbus, DEV_DATA0);
13140Sstevel@tonic-gate 		SMBUS_PRINT((PRT_RD, "smbus_rd: data in = %d\n",
13150Sstevel@tonic-gate 		    tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid]));
13160Sstevel@tonic-gate 		tp->i2c_r_resid--;
13170Sstevel@tonic-gate 		smbus->smbus_bytes_to_read = 0;
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 		if (tp->i2c_r_resid == 0) {
13200Sstevel@tonic-gate 			return (SMBUS_COMPLETE);
13210Sstevel@tonic-gate 		}
13220Sstevel@tonic-gate 	}
13230Sstevel@tonic-gate 
13240Sstevel@tonic-gate 	/*
13250Sstevel@tonic-gate 	 * Address must be re-written for each command.  It must
13260Sstevel@tonic-gate 	 * be written before SMB_TYP.
13270Sstevel@tonic-gate 	 */
13280Sstevel@tonic-gate 	smbus_put(smbus, DEV_ADDR, addr | I2C_READ, 0);
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate 	if (tp->i2c_r_resid == 0) {
13310Sstevel@tonic-gate 		smbus->smbus_bytes_to_read = 0;
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate 		return (SMBUS_COMPLETE);
13340Sstevel@tonic-gate 	}
13350Sstevel@tonic-gate 
13360Sstevel@tonic-gate 	smbus->smbus_bytes_to_read = 1;
13370Sstevel@tonic-gate 	smbus_put(smbus, SMB_TYP, RCV_BYTE, 0);
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate 	smbus_put(smbus, SMB_STS, 0xff, 0);
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 	SMBUS_PRINT((PRT_RD, "smbus_rd: starting a read addr = %x resid = %d "
13420Sstevel@tonic-gate 	    "bytes_to_read=%d\n", addr, tp->i2c_r_resid,
13430Sstevel@tonic-gate 	    smbus->smbus_bytes_to_read));
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 	smbus_put(smbus, STR_PORT, 0, SMBUS_FLUSH);
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate 	return (SMBUS_PENDING);
13480Sstevel@tonic-gate }
1349