xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/nexus/pcf8584.c (revision f47a9c50)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * pcf8584.c is the nexus driver for all pcf8584 controller
317c478bd9Sstevel@tonic-gate  * implementations.  It supports both interrupt and polled
327c478bd9Sstevel@tonic-gate  * mode operation, but defaults to interrupt.
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/conf.h>
377c478bd9Sstevel@tonic-gate #include <sys/file.h>
387c478bd9Sstevel@tonic-gate #include <sys/open.h>
397c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
407c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
417c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
427c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
437c478bd9Sstevel@tonic-gate #include <sys/stat.h>
447c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
457c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
467c478bd9Sstevel@tonic-gate #include <sys/platform_module.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
497c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h>
507c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc_impl.h>
517c478bd9Sstevel@tonic-gate #include <sys/i2c/nexus/pcf8584.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include <sys/note.h>
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate  * static function declarations
577c478bd9Sstevel@tonic-gate  */
587c478bd9Sstevel@tonic-gate static void pcf8584_resume(dev_info_t *dip);
597c478bd9Sstevel@tonic-gate static void pcf8584_suspend(dev_info_t *dip);
607c478bd9Sstevel@tonic-gate static int pcf8584_bus_ctl(dev_info_t *dip, dev_info_t *rdip,
617c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
627c478bd9Sstevel@tonic-gate static  void pcf8584_acquire(pcf8584_t *, dev_info_t *dip,
637c478bd9Sstevel@tonic-gate 	i2c_transfer_t *tp, boolean_t force);
647c478bd9Sstevel@tonic-gate static  void pcf8584_release(pcf8584_t *, boolean_t force);
657c478bd9Sstevel@tonic-gate static int pcf8584_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
667c478bd9Sstevel@tonic-gate static int pcf8584_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
677c478bd9Sstevel@tonic-gate static int pcf8584_open(dev_t *devp, int flag, int otyp,
687c478bd9Sstevel@tonic-gate     cred_t *cred_p);
697c478bd9Sstevel@tonic-gate static int pcf8584_close(dev_t dev, int flag, int otyp,
707c478bd9Sstevel@tonic-gate     cred_t *cred_p);
717c478bd9Sstevel@tonic-gate static int pcf8584_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
727c478bd9Sstevel@tonic-gate static void pcf8584_select_bus(pcf8584_t *i2c);
737c478bd9Sstevel@tonic-gate static enum tran_state pcf8584_type_to_state(int i2c_flags);
747c478bd9Sstevel@tonic-gate static void pcf8584_put_s1(pcf8584_t *i2c, char cmd);
757c478bd9Sstevel@tonic-gate static void pcf8584_put_s0(pcf8584_t *i2c, char data);
767c478bd9Sstevel@tonic-gate static uint8_t pcf8584_get_s0(pcf8584_t *i2c);
777c478bd9Sstevel@tonic-gate static uint8_t pcf8584_get_s1(pcf8584_t *i2c);
787c478bd9Sstevel@tonic-gate static int pcf8584_bbn_ready(pcf8584_t *i2c);
797c478bd9Sstevel@tonic-gate static int pcf8584_error(int status, uint8_t rdwr, pcf8584_t *i2c);
807c478bd9Sstevel@tonic-gate static void pcf8584_monitor_mode(pcf8584_t *i2c);
817c478bd9Sstevel@tonic-gate static int pcf8584_initchild(dev_info_t *cdip);
827c478bd9Sstevel@tonic-gate static void pcf8584_uninitchild(dev_info_t *cdip);
837c478bd9Sstevel@tonic-gate static void pcf8584_init(pcf8584_t *i2c);
847c478bd9Sstevel@tonic-gate static int pcf8584_setup_regs(dev_info_t *dip, pcf8584_t *i2c);
857c478bd9Sstevel@tonic-gate static void pcf8584_free_regs(pcf8584_t *i2c);
867c478bd9Sstevel@tonic-gate static void pcf8584_reportdev(dev_info_t *dip, dev_info_t *rdip);
877c478bd9Sstevel@tonic-gate static int pcf8584_dip_to_addr(dev_info_t *dip);
887c478bd9Sstevel@tonic-gate static uint_t pcf8584_intr(caddr_t arg);
897c478bd9Sstevel@tonic-gate static int pcf8584_process(pcf8584_t *i2c, uint8_t s1);
907c478bd9Sstevel@tonic-gate int pcf8584_transfer(dev_info_t *dip, i2c_transfer_t *tp);
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate static void pcf8584_do_polled_io(pcf8584_t *i2c);
937c478bd9Sstevel@tonic-gate static void pcf8584_take_over(pcf8584_t *i2c, dev_info_t *dip,
947c478bd9Sstevel@tonic-gate     i2c_transfer_t *tp, kcondvar_t **waiter, int *saved_mode);
957c478bd9Sstevel@tonic-gate static void pcf8584_give_up(pcf8584_t *i2c, kcondvar_t *waiter, int saved_mode);
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static struct bus_ops pcf8584_busops = {
987c478bd9Sstevel@tonic-gate 	BUSO_REV,
997c478bd9Sstevel@tonic-gate 	nullbusmap,			/* bus_map */
1007c478bd9Sstevel@tonic-gate 	NULL,				/* bus_get_intrspec */
1017c478bd9Sstevel@tonic-gate 	NULL,				/* bus_add_intrspec */
1027c478bd9Sstevel@tonic-gate 	NULL,				/* bus_remove_intrspec */
1037c478bd9Sstevel@tonic-gate 	NULL,				/* bus_map_fault */
1047c478bd9Sstevel@tonic-gate 	ddi_no_dma_map,			/* bus_dma_map */
1057c478bd9Sstevel@tonic-gate 	ddi_no_dma_allochdl,		/* bus_dma_allochdl */
1067c478bd9Sstevel@tonic-gate 	ddi_no_dma_freehdl,		/* bus_dma_freehdl */
1077c478bd9Sstevel@tonic-gate 	ddi_no_dma_bindhdl,		/* bus_dma_bindhdl */
1087c478bd9Sstevel@tonic-gate 	ddi_no_dma_unbindhdl,		/* bus_unbindhdl */
1097c478bd9Sstevel@tonic-gate 	ddi_no_dma_flush,		/* bus_dma_flush */
1107c478bd9Sstevel@tonic-gate 	ddi_no_dma_win,			/* bus_dma_win */
1117c478bd9Sstevel@tonic-gate 	ddi_no_dma_mctl,		/* bus_dma_ctl */
1127c478bd9Sstevel@tonic-gate 	pcf8584_bus_ctl,		/* bus_ctl */
1137c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,		/* bus_prop_op */
1147c478bd9Sstevel@tonic-gate 	NULL,				/* bus_get_eventcookie */
1157c478bd9Sstevel@tonic-gate 	NULL,				/* bus_add_eventcall */
1167c478bd9Sstevel@tonic-gate 	NULL,				/* bus_remove_eventcall */
1177c478bd9Sstevel@tonic-gate 	NULL,				/* bus_post_event */
1187c478bd9Sstevel@tonic-gate 	0,				/* bus_intr_ctl */
1197c478bd9Sstevel@tonic-gate 	0,				/* bus_config		*/
1207c478bd9Sstevel@tonic-gate 	0,				/* bus_unconfig		*/
1217c478bd9Sstevel@tonic-gate 	0,				/* bus_fm_init		*/
1227c478bd9Sstevel@tonic-gate 	0,				/* bus_fm_fini		*/
1237c478bd9Sstevel@tonic-gate 	0,				/* bus_fm_access_enter	*/
1247c478bd9Sstevel@tonic-gate 	0,				/* bus_fm_access_exit	*/
1257c478bd9Sstevel@tonic-gate 	0,				/* bus_power		*/
1267c478bd9Sstevel@tonic-gate 	i_ddi_intr_ops			/* bus_intr_op		*/
1277c478bd9Sstevel@tonic-gate };
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate struct cb_ops pcf8584_cb_ops = {
1307c478bd9Sstevel@tonic-gate 	pcf8584_open,		/* open */
1317c478bd9Sstevel@tonic-gate 	pcf8584_close,	/* close */
1327c478bd9Sstevel@tonic-gate 	nodev,			/* strategy */
1337c478bd9Sstevel@tonic-gate 	nodev,			/* print */
1347c478bd9Sstevel@tonic-gate 	nodev,			/* dump */
1357c478bd9Sstevel@tonic-gate 	nodev,			/* read */
1367c478bd9Sstevel@tonic-gate 	nodev,			/* write */
1377c478bd9Sstevel@tonic-gate 	pcf8584_ioctl,		/* ioctl */
1387c478bd9Sstevel@tonic-gate 	nodev,			/* devmap */
1397c478bd9Sstevel@tonic-gate 	nodev,			/* mmap */
1407c478bd9Sstevel@tonic-gate 	nodev,			/* segmap */
1417c478bd9Sstevel@tonic-gate 	nochpoll,		/* poll */
1427c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
1437c478bd9Sstevel@tonic-gate 	0,			/* streamtab  */
1447c478bd9Sstevel@tonic-gate 	D_MP | D_NEW		/* Driver compatibility flag */
1457c478bd9Sstevel@tonic-gate };
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static struct dev_ops pcf8584_ops = {
1487c478bd9Sstevel@tonic-gate 	DEVO_REV,
1497c478bd9Sstevel@tonic-gate 	0,
1507c478bd9Sstevel@tonic-gate 	ddi_getinfo_1to1,
1517c478bd9Sstevel@tonic-gate 	nulldev,
1527c478bd9Sstevel@tonic-gate 	nulldev,
1537c478bd9Sstevel@tonic-gate 	pcf8584_attach,
1547c478bd9Sstevel@tonic-gate 	pcf8584_detach,
1557c478bd9Sstevel@tonic-gate 	nodev,
1567c478bd9Sstevel@tonic-gate 	&pcf8584_cb_ops,
1577c478bd9Sstevel@tonic-gate 	&pcf8584_busops
1587c478bd9Sstevel@tonic-gate };
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
1617c478bd9Sstevel@tonic-gate 	&mod_driverops, /* Type of module. This one is a driver */
1627c478bd9Sstevel@tonic-gate 	"I2C Nexus Driver %I%",	/* Name of the module. */
1637c478bd9Sstevel@tonic-gate 	&pcf8584_ops,		/* driver ops */
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1677c478bd9Sstevel@tonic-gate 	MODREV_1,
1687c478bd9Sstevel@tonic-gate 	&modldrv,
1697c478bd9Sstevel@tonic-gate 	NULL
1707c478bd9Sstevel@tonic-gate };
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate /*
1737c478bd9Sstevel@tonic-gate  * pcf8584 soft state
1747c478bd9Sstevel@tonic-gate  */
1757c478bd9Sstevel@tonic-gate static void	*pcf8584_state;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate i2c_nexus_reg_t pcf8584_regvec = {
1787c478bd9Sstevel@tonic-gate 	I2C_NEXUS_REV,
1797c478bd9Sstevel@tonic-gate 	pcf8584_transfer,
1807c478bd9Sstevel@tonic-gate };
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate /*
1837c478bd9Sstevel@tonic-gate  * The "interrupt_priorities" property is how a driver can specify a SPARC
1847c478bd9Sstevel@tonic-gate  * PIL level to associate with each of its interrupt properties.  Most
1857c478bd9Sstevel@tonic-gate  * self-identifying busses have a better mechanism for managing this, but I2C
1867c478bd9Sstevel@tonic-gate  * doesn't.
1877c478bd9Sstevel@tonic-gate  */
1887c478bd9Sstevel@tonic-gate int	pcf8584_pil = PCF8584_PIL;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate #ifdef DEBUG
1917c478bd9Sstevel@tonic-gate int pcf8584_print_lvl = 0;
1927c478bd9Sstevel@tonic-gate static kmutex_t msg_buf_lock;
1937c478bd9Sstevel@tonic-gate static char msg_buff[1024];
1947c478bd9Sstevel@tonic-gate #define	PCF8584_DDB(command)	\
1957c478bd9Sstevel@tonic-gate 	do {			\
1967c478bd9Sstevel@tonic-gate 		{ command; }	\
1977c478bd9Sstevel@tonic-gate 		_NOTE(CONSTANTCONDITION)	\
1987c478bd9Sstevel@tonic-gate 	} while (0)
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate static void
2017c478bd9Sstevel@tonic-gate pcf8584_print(int flags, const char *fmt, ...)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	if (flags & pcf8584_print_lvl) {
2047c478bd9Sstevel@tonic-gate 		va_list ap;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 		va_start(ap, fmt);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 		if (pcf8584_print_lvl & PRT_PROM) {
2097c478bd9Sstevel@tonic-gate 			prom_vprintf(fmt, ap);
2107c478bd9Sstevel@tonic-gate 		} else {
2117c478bd9Sstevel@tonic-gate 			mutex_enter(&msg_buf_lock);
2127c478bd9Sstevel@tonic-gate 			(void) vsprintf(msg_buff, fmt, ap);
2137c478bd9Sstevel@tonic-gate 			if (pcf8584_print_lvl & PRT_BUFFONLY) {
2147c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT, "?%s", msg_buff);
2157c478bd9Sstevel@tonic-gate 			} else {
2167c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT, "%s", msg_buff);
2177c478bd9Sstevel@tonic-gate 			}
2187c478bd9Sstevel@tonic-gate 			mutex_exit(&msg_buf_lock);
2197c478bd9Sstevel@tonic-gate 		}
2207c478bd9Sstevel@tonic-gate 		va_end(ap);
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate }
2237c478bd9Sstevel@tonic-gate #else
2247c478bd9Sstevel@tonic-gate #define	PCF8584_DDB(command) \
2257c478bd9Sstevel@tonic-gate 	do {			\
2267c478bd9Sstevel@tonic-gate 		{ _NOTE(EMPTY); }	\
2277c478bd9Sstevel@tonic-gate 		_NOTE(CONSTANTCONDITION)	\
2287c478bd9Sstevel@tonic-gate 	} while (0)
2297c478bd9Sstevel@tonic-gate #endif
2307c478bd9Sstevel@tonic-gate 
231afd7fd7bSosaeed #define	PCF8584_IMPL_DELAY(type, delay)	\
232afd7fd7bSosaeed 	if (type == PIC16F747) {	\
233afd7fd7bSosaeed 		drv_usecwait(delay);	\
234afd7fd7bSosaeed 	}
235afd7fd7bSosaeed 
2367c478bd9Sstevel@tonic-gate int
2377c478bd9Sstevel@tonic-gate _init(void)
2387c478bd9Sstevel@tonic-gate {
2397c478bd9Sstevel@tonic-gate 	int status;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	status = ddi_soft_state_init(&pcf8584_state, sizeof (pcf8584_t),
2427c478bd9Sstevel@tonic-gate 		PCF8584_INITIAL_SOFT_SPACE);
2437c478bd9Sstevel@tonic-gate 	if (status != 0) {
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		return (status);
2467c478bd9Sstevel@tonic-gate 	}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	if ((status = mod_install(&modlinkage)) != 0) {
2497c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&pcf8584_state);
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	return (status);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate int
2567c478bd9Sstevel@tonic-gate _fini(void)
2577c478bd9Sstevel@tonic-gate {
2587c478bd9Sstevel@tonic-gate 	int status;
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	if ((status = mod_remove(&modlinkage)) == 0) {
2617c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&pcf8584_state);
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	return (status);
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate /*
2687c478bd9Sstevel@tonic-gate  * The loadable-module _info(9E) entry point
2697c478bd9Sstevel@tonic-gate  */
2707c478bd9Sstevel@tonic-gate int
2717c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2727c478bd9Sstevel@tonic-gate {
2737c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2747c478bd9Sstevel@tonic-gate }
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate static void
2777c478bd9Sstevel@tonic-gate pcf8584_dodetach(dev_info_t *dip)
2787c478bd9Sstevel@tonic-gate {
2797c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
2807c478bd9Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance);
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & ADD_INTR) != 0) {
2857c478bd9Sstevel@tonic-gate 		ddi_remove_intr(dip, 0, i2c->pcf8584_icookie);
2867c478bd9Sstevel@tonic-gate 	}
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	cv_destroy(&i2c->pcf8584_cv);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & IMUTEX) != 0) {
2917c478bd9Sstevel@tonic-gate 		mutex_destroy(&i2c->pcf8584_imutex);
2927c478bd9Sstevel@tonic-gate 		    cv_destroy(&i2c->pcf8584_icv);
2937c478bd9Sstevel@tonic-gate 	}
2947c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & SETUP_REGS) != 0) {
2957c478bd9Sstevel@tonic-gate 		pcf8584_free_regs(i2c);
2967c478bd9Sstevel@tonic-gate 	}
2977c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & NEXUS_REGISTER) != 0) {
2987c478bd9Sstevel@tonic-gate 		i2c_nexus_unregister(dip);
2997c478bd9Sstevel@tonic-gate 	}
3007c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & PROP_CREATE) != 0) {
3017c478bd9Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
3027c478bd9Sstevel@tonic-gate 			"interrupt-priorities");
3037c478bd9Sstevel@tonic-gate 	}
3047c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & MINOR_NODE) != 0) {
3057c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
3067c478bd9Sstevel@tonic-gate 	}
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(pcf8584_state, instance);
3097c478bd9Sstevel@tonic-gate }
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate static int
3127c478bd9Sstevel@tonic-gate pcf8584_doattach(dev_info_t *dip)
3137c478bd9Sstevel@tonic-gate {
3147c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
3157c478bd9Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/*
3187c478bd9Sstevel@tonic-gate 	 * Allocate soft state structure.
3197c478bd9Sstevel@tonic-gate 	 */
3207c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(pcf8584_state, instance) != DDI_SUCCESS) {
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3237c478bd9Sstevel@tonic-gate 	}
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	i2c->pcf8584_dip = dip;
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	(void) snprintf(i2c->pcf8584_name, sizeof (i2c->pcf8584_name),
3307c478bd9Sstevel@tonic-gate 		"%s_%d", ddi_node_name(dip), instance);
3317c478bd9Sstevel@tonic-gate 
332afd7fd7bSosaeed 	/*
333afd7fd7bSosaeed 	 * Identify which pcf8584 implementation is being attached to.
334afd7fd7bSosaeed 	 */
335afd7fd7bSosaeed 	if (strcmp(ddi_binding_name(i2c->pcf8584_dip), "SUNW,bbc-i2c") == 0) {
336afd7fd7bSosaeed 		i2c->pcf8584_impl_type = BBC;
337afd7fd7bSosaeed 		i2c->pcf8584_impl_delay = PCF8584_GENERIC_DELAY;
338afd7fd7bSosaeed 	} else if (strcmp(ddi_binding_name(i2c->pcf8584_dip),
339afd7fd7bSosaeed 	    "SUNW,i2c-pic16f747") == 0) {
340afd7fd7bSosaeed 		i2c->pcf8584_impl_type = PIC16F747;
341afd7fd7bSosaeed 		i2c->pcf8584_impl_delay = PCF8584_PIC16F747_DELAY;
342afd7fd7bSosaeed 	} else {
343afd7fd7bSosaeed 		i2c->pcf8584_impl_type = GENERIC;
344afd7fd7bSosaeed 		i2c->pcf8584_impl_delay = PCF8584_GENERIC_DELAY;
345afd7fd7bSosaeed 	}
346afd7fd7bSosaeed 
3477c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
3487c478bd9Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
3497c478bd9Sstevel@tonic-gate 	    "interrupt-priorities") != 1) {
3507c478bd9Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
3517c478bd9Sstevel@tonic-gate 			DDI_PROP_CANSLEEP, "interrupt-priorities",
3527c478bd9Sstevel@tonic-gate 			(caddr_t)&pcf8584_pil,
3537c478bd9Sstevel@tonic-gate 			sizeof (pcf8584_pil));
3547c478bd9Sstevel@tonic-gate 		i2c->pcf8584_attachflags |= PROP_CREATE;
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	cv_init(&i2c->pcf8584_cv, NULL, CV_DRIVER, NULL);
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	if (pcf8584_setup_regs(dip, i2c) != DDI_SUCCESS) {
3607c478bd9Sstevel@tonic-gate 		goto bad;
3617c478bd9Sstevel@tonic-gate 	}
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	i2c->pcf8584_attachflags |= SETUP_REGS;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
3667c478bd9Sstevel@tonic-gate 	    DDI_PROP_CANSLEEP, "poll-mode") == 1) {
3677c478bd9Sstevel@tonic-gate 		i2c->pcf8584_mode = PCF8584_POLL_MODE;
3687c478bd9Sstevel@tonic-gate 	} else {
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 		if (ddi_get_iblock_cookie(dip, 0,
3717c478bd9Sstevel@tonic-gate 		    &i2c->pcf8584_icookie) == DDI_SUCCESS) {
3727c478bd9Sstevel@tonic-gate 			mutex_init(&i2c->pcf8584_imutex, NULL, MUTEX_DRIVER,
3737c478bd9Sstevel@tonic-gate 			    (void *)i2c->pcf8584_icookie);
3747c478bd9Sstevel@tonic-gate 			cv_init(&i2c->pcf8584_icv, NULL, CV_DRIVER, NULL);
3757c478bd9Sstevel@tonic-gate 			i2c->pcf8584_attachflags |= IMUTEX;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 			if (ddi_add_intr(dip, 0, NULL, NULL, pcf8584_intr,
3787c478bd9Sstevel@tonic-gate 			    (caddr_t)i2c) == DDI_SUCCESS) {
3797c478bd9Sstevel@tonic-gate 				i2c->pcf8584_attachflags |= ADD_INTR;
3807c478bd9Sstevel@tonic-gate 				i2c->pcf8584_mode = PCF8584_INTR_MODE;
3817c478bd9Sstevel@tonic-gate 			} else {
3827c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s failed to add interrupt",
3837c478bd9Sstevel@tonic-gate 				    i2c->pcf8584_name);
3847c478bd9Sstevel@tonic-gate 				i2c->pcf8584_mode = PCF8584_POLL_MODE;
3857c478bd9Sstevel@tonic-gate 			}
3867c478bd9Sstevel@tonic-gate 		} else {
3877c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s failed to retrieve iblock cookie. "
3887c478bd9Sstevel@tonic-gate 			    "Operating in POLL MODE only", i2c->pcf8584_name);
3897c478bd9Sstevel@tonic-gate 			i2c->pcf8584_mode = PCF8584_POLL_MODE;
3907c478bd9Sstevel@tonic-gate 		}
3917c478bd9Sstevel@tonic-gate 	}
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	/*
3947c478bd9Sstevel@tonic-gate 	 * For polled mode, still initialize a cv and mutex
3957c478bd9Sstevel@tonic-gate 	 */
3967c478bd9Sstevel@tonic-gate 	if ((i2c->pcf8584_attachflags & IMUTEX) == 0) {
3977c478bd9Sstevel@tonic-gate 		cv_init(&i2c->pcf8584_icv, NULL, CV_DRIVER, NULL);
3987c478bd9Sstevel@tonic-gate 		mutex_init(&i2c->pcf8584_imutex, NULL, MUTEX_DRIVER, NULL);
3997c478bd9Sstevel@tonic-gate 		i2c->pcf8584_attachflags |= IMUTEX;
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	i2c_nexus_register(dip, &pcf8584_regvec);
4037c478bd9Sstevel@tonic-gate 	i2c->pcf8584_attachflags |= NEXUS_REGISTER;
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
4067c478bd9Sstevel@tonic-gate 	    DDI_NT_NEXUS, 0) == DDI_FAILURE) {
4077c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed",
4087c478bd9Sstevel@tonic-gate 		    i2c->pcf8584_name);
4097c478bd9Sstevel@tonic-gate 		goto bad;
4107c478bd9Sstevel@tonic-gate 	}
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	i2c->pcf8584_attachflags |= MINOR_NODE;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	pcf8584_init(i2c);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	i2c->pcf8584_nexus_dip = dip;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate bad:
4217c478bd9Sstevel@tonic-gate 	pcf8584_dodetach(dip);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
4247c478bd9Sstevel@tonic-gate }
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate static int
4277c478bd9Sstevel@tonic-gate pcf8584_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4287c478bd9Sstevel@tonic-gate {
4297c478bd9Sstevel@tonic-gate 	switch (cmd) {
4307c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 		return (pcf8584_doattach(dip));
4337c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
4347c478bd9Sstevel@tonic-gate 		pcf8584_resume(dip);
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4377c478bd9Sstevel@tonic-gate 	default:
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4407c478bd9Sstevel@tonic-gate 	}
4417c478bd9Sstevel@tonic-gate }
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate static int
4447c478bd9Sstevel@tonic-gate pcf8584_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4457c478bd9Sstevel@tonic-gate {
4467c478bd9Sstevel@tonic-gate 	switch (cmd) {
4477c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
4487c478bd9Sstevel@tonic-gate 		pcf8584_dodetach(dip);
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4517c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
4527c478bd9Sstevel@tonic-gate 		pcf8584_suspend(dip);
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4557c478bd9Sstevel@tonic-gate 	default:
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4587c478bd9Sstevel@tonic-gate 	}
4597c478bd9Sstevel@tonic-gate }
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4627c478bd9Sstevel@tonic-gate static int
4637c478bd9Sstevel@tonic-gate pcf8584_open(dev_t  *devp,  int  flag,  int  otyp,  cred_t *cred_p)
4647c478bd9Sstevel@tonic-gate {
4657c478bd9Sstevel@tonic-gate 	int instance;
4667c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	/*
4697c478bd9Sstevel@tonic-gate 	 * Make sure the open is for the right file type
4707c478bd9Sstevel@tonic-gate 	 */
4717c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
4727c478bd9Sstevel@tonic-gate 		return (EINVAL);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	instance = getminor(*devp);
4757c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance);
4767c478bd9Sstevel@tonic-gate 	if (i2c == NULL)
4777c478bd9Sstevel@tonic-gate 		return (ENXIO);
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	/*
4807c478bd9Sstevel@tonic-gate 	 * Enforce exclusive access
4817c478bd9Sstevel@tonic-gate 	 */
4827c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
4837c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_open) {
4847c478bd9Sstevel@tonic-gate 		mutex_exit(&i2c->pcf8584_imutex);
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 		return (EBUSY);
4877c478bd9Sstevel@tonic-gate 	} else
4887c478bd9Sstevel@tonic-gate 		i2c->pcf8584_open = 1;
4897c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	return (0);
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4957c478bd9Sstevel@tonic-gate static int
4967c478bd9Sstevel@tonic-gate pcf8584_close(dev_t  dev,  int  flag,  int  otyp,  cred_t *cred_p)
4977c478bd9Sstevel@tonic-gate {
4987c478bd9Sstevel@tonic-gate 	int instance;
4997c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	/*
5027c478bd9Sstevel@tonic-gate 	 * Make sure the close is for the right file type
5037c478bd9Sstevel@tonic-gate 	 */
5047c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
5057c478bd9Sstevel@tonic-gate 		return (EINVAL);
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	instance = getminor(dev);
5087c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance);
5097c478bd9Sstevel@tonic-gate 	if (i2c == NULL)
5107c478bd9Sstevel@tonic-gate 		return (ENXIO);
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
5137c478bd9Sstevel@tonic-gate 	i2c->pcf8584_open = 0;
5147c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	return (0);
5177c478bd9Sstevel@tonic-gate }
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5207c478bd9Sstevel@tonic-gate static int
5217c478bd9Sstevel@tonic-gate pcf8584_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
5227c478bd9Sstevel@tonic-gate 	int *rvalp)
5237c478bd9Sstevel@tonic-gate {
5247c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
5257c478bd9Sstevel@tonic-gate 	dev_info_t *self;
5267c478bd9Sstevel@tonic-gate 	struct devctl_iocdata *dcp;
5277c478bd9Sstevel@tonic-gate 	int rv;
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, getminor(dev));
5307c478bd9Sstevel@tonic-gate 	if (i2c == NULL)
5317c478bd9Sstevel@tonic-gate 		return (ENXIO);
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	self = (dev_info_t *)i2c->pcf8584_nexus_dip;
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	/*
5367c478bd9Sstevel@tonic-gate 	 * read devctl ioctl data
5377c478bd9Sstevel@tonic-gate 	 */
5387c478bd9Sstevel@tonic-gate 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) {
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 		return (EFAULT);
5417c478bd9Sstevel@tonic-gate 	}
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	switch (cmd) {
5447c478bd9Sstevel@tonic-gate 		case DEVCTL_BUS_DEV_CREATE:
5457c478bd9Sstevel@tonic-gate 			rv = ndi_dc_devi_create(dcp, self, 0, NULL);
5467c478bd9Sstevel@tonic-gate 			break;
5477c478bd9Sstevel@tonic-gate 		case DEVCTL_DEVICE_REMOVE:
5487c478bd9Sstevel@tonic-gate 			rv = ndi_devctl_device_remove(self, dcp, 0);
5497c478bd9Sstevel@tonic-gate 			break;
5507c478bd9Sstevel@tonic-gate 		default:
5517c478bd9Sstevel@tonic-gate 			rv = ENOTSUP;
5527c478bd9Sstevel@tonic-gate 	}
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	ndi_dc_freehdl(dcp);
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	return (rv);
5577c478bd9Sstevel@tonic-gate }
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate static int
5607c478bd9Sstevel@tonic-gate pcf8584_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
5617c478bd9Sstevel@tonic-gate     void *arg, void *result)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate 	switch (op) {
5647c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 		return (pcf8584_initchild((dev_info_t *)arg));
5677c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
5687c478bd9Sstevel@tonic-gate 		pcf8584_uninitchild((dev_info_t *)arg);
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
5717c478bd9Sstevel@tonic-gate 	CTLOPS_REPORTDEV:
5727c478bd9Sstevel@tonic-gate 		pcf8584_reportdev(dip, rdip);
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
5757c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_DMAPMAPC:
5767c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_POKE:
5777c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_PEEK:
5787c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_IOMIN:
5797c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTINT:
5807c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_SIDDEV:
5817c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_SLAVEONLY:
5827c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_AFFINITY:
5837c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_PTOB:
5847c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_BTOP:
5857c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_BTOPR:
5867c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_DVMAPAGESIZE:
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
5897c478bd9Sstevel@tonic-gate 	default:
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate /*
5967c478bd9Sstevel@tonic-gate  * pcf8584_suspend() is called before the system suspends.  Existing
5977c478bd9Sstevel@tonic-gate  * transfer in progress or waiting will complete, but new transfers are
5987c478bd9Sstevel@tonic-gate  * effectively blocked by "acquiring" the bus.
5997c478bd9Sstevel@tonic-gate  */
6007c478bd9Sstevel@tonic-gate static void
6017c478bd9Sstevel@tonic-gate pcf8584_suspend(dev_info_t *dip)
6027c478bd9Sstevel@tonic-gate {
6037c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
6047c478bd9Sstevel@tonic-gate 	int instance;
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6077c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	pcf8584_acquire(i2c, NULL, NULL, B_FALSE);
6107c478bd9Sstevel@tonic-gate }
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate /*
6137c478bd9Sstevel@tonic-gate  * pcf8584_resume() is called when the system resumes from CPR.  It releases
6147c478bd9Sstevel@tonic-gate  * the hold that was placed on the i2c bus, which allows any real
6157c478bd9Sstevel@tonic-gate  * transfers to continue.
6167c478bd9Sstevel@tonic-gate  */
6177c478bd9Sstevel@tonic-gate static void
6187c478bd9Sstevel@tonic-gate pcf8584_resume(dev_info_t *dip)
6197c478bd9Sstevel@tonic-gate {
6207c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
6217c478bd9Sstevel@tonic-gate 	int instance;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6247c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	pcf8584_release(i2c, B_FALSE);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	pcf8584_init(i2c);
6297c478bd9Sstevel@tonic-gate }
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate /*
6327c478bd9Sstevel@tonic-gate  * pcf8584_acquire() is called by a thread wishing to "own" the I2C bus.
6337c478bd9Sstevel@tonic-gate  * It should not be held across multiple transfers. If the 'force' flag
6347c478bd9Sstevel@tonic-gate  * is set, do not try to acquire mutex or do cv_wait.
6357c478bd9Sstevel@tonic-gate  */
6367c478bd9Sstevel@tonic-gate static void
6377c478bd9Sstevel@tonic-gate pcf8584_acquire(pcf8584_t *i2c, dev_info_t *dip, i2c_transfer_t *tp,
6387c478bd9Sstevel@tonic-gate     boolean_t force)
6397c478bd9Sstevel@tonic-gate {
6407c478bd9Sstevel@tonic-gate 	if (force) {
6417c478bd9Sstevel@tonic-gate 		i2c->pcf8584_busy = 1;
6427c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_tran = tp;
6437c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_dip = dip;
6447c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_status = PCF8584_TRANSFER_NEW;
6457c478bd9Sstevel@tonic-gate 		return;
6467c478bd9Sstevel@tonic-gate 	}
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
6497c478bd9Sstevel@tonic-gate 	while (i2c->pcf8584_busy) {
6507c478bd9Sstevel@tonic-gate 		cv_wait(&i2c->pcf8584_cv, &i2c->pcf8584_imutex);
6517c478bd9Sstevel@tonic-gate 	}
6527c478bd9Sstevel@tonic-gate 	i2c->pcf8584_busy = 1;
6537c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
6547c478bd9Sstevel@tonic-gate 	/*
6557c478bd9Sstevel@tonic-gate 	 * On systems where OBP shares a pcf8584 controller with the
6567c478bd9Sstevel@tonic-gate 	 * OS, plat_shared_i2c_enter will serialize access to the
6577c478bd9Sstevel@tonic-gate 	 * pcf8584 controller.  Do not grab this lock during CPR
6587c478bd9Sstevel@tonic-gate 	 * suspend as the CPR thread also acquires this muxex
6597c478bd9Sstevel@tonic-gate 	 * through through prom_setprop which causes recursive
6607c478bd9Sstevel@tonic-gate 	 * mutex enter.
6617c478bd9Sstevel@tonic-gate 	 *
6627c478bd9Sstevel@tonic-gate 	 * dip == NULL during CPR.
6637c478bd9Sstevel@tonic-gate 	 */
6647c478bd9Sstevel@tonic-gate 	if ((&plat_shared_i2c_enter != NULL) && (dip != NULL)) {
6657c478bd9Sstevel@tonic-gate 		plat_shared_i2c_enter(i2c->pcf8584_dip);
6667c478bd9Sstevel@tonic-gate 	}
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
6697c478bd9Sstevel@tonic-gate 	i2c->pcf8584_cur_tran = tp;
6707c478bd9Sstevel@tonic-gate 	i2c->pcf8584_cur_dip = dip;
6717c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
6727c478bd9Sstevel@tonic-gate }
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate /*
6757c478bd9Sstevel@tonic-gate  * pcf8584_release() is called to release a hold made by pcf8584_acquire().
6767c478bd9Sstevel@tonic-gate  */
6777c478bd9Sstevel@tonic-gate static void
6787c478bd9Sstevel@tonic-gate pcf8584_release(pcf8584_t *i2c, boolean_t force)
6797c478bd9Sstevel@tonic-gate {
6807c478bd9Sstevel@tonic-gate 	if (force) {
6817c478bd9Sstevel@tonic-gate 		i2c->pcf8584_busy = 0;
6827c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_tran = NULL;
6837c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_dip = NULL;
6847c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_status = PCF8584_TRANSFER_OVER;
6857c478bd9Sstevel@tonic-gate 		cv_signal(&i2c->pcf8584_cv);
6867c478bd9Sstevel@tonic-gate 		return;
6877c478bd9Sstevel@tonic-gate 	}
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
6907c478bd9Sstevel@tonic-gate 	i2c->pcf8584_busy = 0;
6917c478bd9Sstevel@tonic-gate 	i2c->pcf8584_cur_tran = NULL;
6927c478bd9Sstevel@tonic-gate 	cv_signal(&i2c->pcf8584_cv);
6937c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	if ((&plat_shared_i2c_exit != NULL) && (i2c->pcf8584_cur_dip != NULL)) {
6967c478bd9Sstevel@tonic-gate 		plat_shared_i2c_exit(i2c->pcf8584_dip);
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate }
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate /*
7017c478bd9Sstevel@tonic-gate  * if pcf8584_b_reg exists, it means the current bus controller signals
7027c478bd9Sstevel@tonic-gate  * are multiplexed into more than a single bus.  Select the bus needed
7037c478bd9Sstevel@tonic-gate  * by writing to the mux register.
7047c478bd9Sstevel@tonic-gate  */
7057c478bd9Sstevel@tonic-gate static void
7067c478bd9Sstevel@tonic-gate pcf8584_select_bus(pcf8584_t *i2c)
7077c478bd9Sstevel@tonic-gate {
7087c478bd9Sstevel@tonic-gate 	int bus;
7097c478bd9Sstevel@tonic-gate 	pcf8584_ppvt_t *ppvt;
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	/*
7127c478bd9Sstevel@tonic-gate 	 * The existence of pcf8584_b_reg means the bus registers
7137c478bd9Sstevel@tonic-gate 	 * are multiplexed.
7147c478bd9Sstevel@tonic-gate 	 */
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	PCF8584_DDB(pcf8584_print(PRT_SELECT, "bus multiplex: %X\n",
7177c478bd9Sstevel@tonic-gate 	    i2c->pcf8584_b_reg));
7187c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_b_reg != NULL) {
7197c478bd9Sstevel@tonic-gate 		ppvt = ddi_get_parent_data(i2c->pcf8584_cur_dip);
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 		bus = ppvt->pcf8584_ppvt_bus;
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 		PCF8584_DDB(pcf8584_print(PRT_SELECT,
7247c478bd9Sstevel@tonic-gate 		    "transmitting bus number %d\n", bus));
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		ddi_put8(i2c->pcf8584_b_rhandle, i2c->pcf8584_b_reg, bus);
7277c478bd9Sstevel@tonic-gate 	}
7287c478bd9Sstevel@tonic-gate }
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate /*
7317c478bd9Sstevel@tonic-gate  * pcf8584_type_to_state() converts a transfer type to the
7327c478bd9Sstevel@tonic-gate  * next state of the I2C state machine based on the requested
7337c478bd9Sstevel@tonic-gate  * transfer type.
7347c478bd9Sstevel@tonic-gate  */
7357c478bd9Sstevel@tonic-gate static enum tran_state
7367c478bd9Sstevel@tonic-gate pcf8584_type_to_state(int i2c_flags)
7377c478bd9Sstevel@tonic-gate {
7387c478bd9Sstevel@tonic-gate 	switch (i2c_flags) {
7397c478bd9Sstevel@tonic-gate 	case I2C_WR:
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		return (TRAN_STATE_WR);
7427c478bd9Sstevel@tonic-gate 	case I2C_RD:
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 		return (TRAN_STATE_DUMMY_RD);
7457c478bd9Sstevel@tonic-gate 	case I2C_WR_RD:
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 		return (TRAN_STATE_WR_RD);
7487c478bd9Sstevel@tonic-gate 	}
749*f47a9c50Smathue 	/*NOTREACHED*/
750*f47a9c50Smathue 	return (TRAN_STATE_NULL);
7517c478bd9Sstevel@tonic-gate }
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate /*
7547c478bd9Sstevel@tonic-gate  * pcf8584_put_s1() writes out cmd to register S1.
7557c478bd9Sstevel@tonic-gate  */
7567c478bd9Sstevel@tonic-gate static void
7577c478bd9Sstevel@tonic-gate pcf8584_put_s1(pcf8584_t *i2c, char cmd)
7587c478bd9Sstevel@tonic-gate {
7597c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t hp = i2c->pcf8584_rhandle;
7607c478bd9Sstevel@tonic-gate 	pcf8584_regs_t *rp = &i2c->pcf8584_regs;
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	ddi_put8(hp, rp->pcf8584_regs_s1, cmd);
763afd7fd7bSosaeed 	PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type,
764afd7fd7bSosaeed 	    i2c->pcf8584_impl_delay);
7657c478bd9Sstevel@tonic-gate 	/*
7667c478bd9Sstevel@tonic-gate 	 * read status to make sure write is flushed
7677c478bd9Sstevel@tonic-gate 	 */
7687c478bd9Sstevel@tonic-gate 	(void) ddi_get8(hp, rp->pcf8584_regs_s1);
769afd7fd7bSosaeed 	PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type,
770afd7fd7bSosaeed 	    i2c->pcf8584_impl_delay);
7717c478bd9Sstevel@tonic-gate }
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate /*
7747c478bd9Sstevel@tonic-gate  * pcf8584_put_s0() writes out data to register S0.
7757c478bd9Sstevel@tonic-gate  */
7767c478bd9Sstevel@tonic-gate static void
7777c478bd9Sstevel@tonic-gate pcf8584_put_s0(pcf8584_t *i2c, char data)
7787c478bd9Sstevel@tonic-gate {
7797c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t hp = i2c->pcf8584_rhandle;
7807c478bd9Sstevel@tonic-gate 	pcf8584_regs_t *rp = &i2c->pcf8584_regs;
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	ddi_put8(hp, rp->pcf8584_regs_s0, data);
783afd7fd7bSosaeed 	PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type,
784afd7fd7bSosaeed 	    i2c->pcf8584_impl_delay);
7857c478bd9Sstevel@tonic-gate 	/*
7867c478bd9Sstevel@tonic-gate 	 * read status to make sure write is flushed
7877c478bd9Sstevel@tonic-gate 	 */
7887c478bd9Sstevel@tonic-gate 	(void) ddi_get8(hp, rp->pcf8584_regs_s1);
789afd7fd7bSosaeed 	PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type,
790afd7fd7bSosaeed 	    i2c->pcf8584_impl_delay);
7917c478bd9Sstevel@tonic-gate }
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate /*
7947c478bd9Sstevel@tonic-gate  * pcf8584_get_s0() reads from register S0.
7957c478bd9Sstevel@tonic-gate  */
7967c478bd9Sstevel@tonic-gate static uint8_t
7977c478bd9Sstevel@tonic-gate pcf8584_get_s0(pcf8584_t *i2c)
7987c478bd9Sstevel@tonic-gate {
7997c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t hp = i2c->pcf8584_rhandle;
8007c478bd9Sstevel@tonic-gate 	pcf8584_regs_t *rp = &i2c->pcf8584_regs;
801afd7fd7bSosaeed 	uint8_t s0;
8027c478bd9Sstevel@tonic-gate 
803afd7fd7bSosaeed 	s0 = ddi_get8(hp, rp->pcf8584_regs_s0);
804afd7fd7bSosaeed 	PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type,
805afd7fd7bSosaeed 	    i2c->pcf8584_impl_delay);
806afd7fd7bSosaeed 
807afd7fd7bSosaeed 	return (s0);
8087c478bd9Sstevel@tonic-gate }
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate /*
8117c478bd9Sstevel@tonic-gate  * pcf8584_get_s1() reads from register S1.
8127c478bd9Sstevel@tonic-gate  */
8137c478bd9Sstevel@tonic-gate static uint8_t
8147c478bd9Sstevel@tonic-gate pcf8584_get_s1(pcf8584_t *i2c)
8157c478bd9Sstevel@tonic-gate {
8167c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t hp = i2c->pcf8584_rhandle;
8177c478bd9Sstevel@tonic-gate 	pcf8584_regs_t *rp = &i2c->pcf8584_regs;
818afd7fd7bSosaeed 	uint8_t s1;
8197c478bd9Sstevel@tonic-gate 
820afd7fd7bSosaeed 	s1 = ddi_get8(hp, rp->pcf8584_regs_s1);
821afd7fd7bSosaeed 	PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type,
822afd7fd7bSosaeed 	    i2c->pcf8584_impl_delay);
823afd7fd7bSosaeed 
824afd7fd7bSosaeed 	return (s1);
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate /*
8287c478bd9Sstevel@tonic-gate  * If the previous transaction was a write, the stop
8297c478bd9Sstevel@tonic-gate  * bit may not make it out on the wire before
8307c478bd9Sstevel@tonic-gate  * the next transaction startes.  And unfortunately, there
8317c478bd9Sstevel@tonic-gate  * is no interrupt after the stop bit is written, so this
8327c478bd9Sstevel@tonic-gate  * function will poll to make sure the BBC is ready.
8337c478bd9Sstevel@tonic-gate  */
8347c478bd9Sstevel@tonic-gate static int
8357c478bd9Sstevel@tonic-gate pcf8584_bbn_ready(pcf8584_t *i2c)
8367c478bd9Sstevel@tonic-gate {
8377c478bd9Sstevel@tonic-gate 	uint8_t s1;
8387c478bd9Sstevel@tonic-gate 	int usecwaits = 0;
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate 	s1 = pcf8584_get_s1(i2c);
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	while ((s1 & S1_BBN) == 0) {
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 		if (usecwaits++ == 100) {
8457c478bd9Sstevel@tonic-gate 			/* Try initializing the bus */
8467c478bd9Sstevel@tonic-gate 			pcf8584_monitor_mode(i2c);
8477c478bd9Sstevel@tonic-gate 			pcf8584_put_s1(i2c, S1_STOP);
8487c478bd9Sstevel@tonic-gate 			delay(1);
8497c478bd9Sstevel@tonic-gate 			pcf8584_init(i2c);
8507c478bd9Sstevel@tonic-gate 			(void) pcf8584_get_s0(i2c);
8517c478bd9Sstevel@tonic-gate 			s1 = pcf8584_get_s1(i2c);
8527c478bd9Sstevel@tonic-gate 			if (s1 & S1_BBN) {
8537c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN,
8547c478bd9Sstevel@tonic-gate 				    "!%s: cleared bus busy.   addr=0x%x",
8557c478bd9Sstevel@tonic-gate 				    i2c->pcf8584_name,
8567c478bd9Sstevel@tonic-gate 				    pcf8584_dip_to_addr(i2c->pcf8584_cur_dip));
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 				return (I2C_SUCCESS);
8597c478bd9Sstevel@tonic-gate 			} else {
8607c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN,
8617c478bd9Sstevel@tonic-gate 				    "!%s bus busy after init addr=0x%x",
8627c478bd9Sstevel@tonic-gate 				    i2c->pcf8584_name,
8637c478bd9Sstevel@tonic-gate 				    pcf8584_dip_to_addr(i2c->pcf8584_cur_dip));
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 				return (I2C_FAILURE);
8667c478bd9Sstevel@tonic-gate 			}
8677c478bd9Sstevel@tonic-gate 		}
8687c478bd9Sstevel@tonic-gate 		drv_usecwait(1);
8697c478bd9Sstevel@tonic-gate 		s1 = pcf8584_get_s1(i2c);
8707c478bd9Sstevel@tonic-gate 	}
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	return (I2C_SUCCESS);
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate static int
8767c478bd9Sstevel@tonic-gate pcf8584_error(int status, uint8_t rdwr, pcf8584_t *i2c)
8777c478bd9Sstevel@tonic-gate {
8787c478bd9Sstevel@tonic-gate 	int addr = pcf8584_dip_to_addr(i2c->pcf8584_cur_dip);
8797c478bd9Sstevel@tonic-gate 	pcf8584_regs_t *rp = &i2c->pcf8584_regs;
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 	if (status & S1_BER) {
8827c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
883*f47a9c50Smathue 		    "!%s bus error; Controller = 0x%p "
8847c478bd9Sstevel@tonic-gate 		    " addr = 0x%x", i2c->pcf8584_name,
885*f47a9c50Smathue 		    (void *)rp->pcf8584_regs_s1, addr);
8867c478bd9Sstevel@tonic-gate 		pcf8584_init(i2c);
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 		return (I2C_FAILURE);
8897c478bd9Sstevel@tonic-gate 	} else if (status & S1_LAB) {
8907c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "!%s lost arbitration; Controller ="
891*f47a9c50Smathue 		    " 0x%p addr = 0x%x", i2c->pcf8584_name,
892*f47a9c50Smathue 		    (void *)rp->pcf8584_regs_s1, addr);
8937c478bd9Sstevel@tonic-gate 		pcf8584_init(i2c);
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 		return (I2C_FAILURE);
8967c478bd9Sstevel@tonic-gate 	} else if ((status & S1_LRB) && (rdwr == I2C_WR)) {
8977c478bd9Sstevel@tonic-gate 		/*
8987c478bd9Sstevel@tonic-gate 		 * No error logged here, because this may be benign.
8997c478bd9Sstevel@tonic-gate 		 * Cf. the "Alert Response Address" feature of SMBUS.
9007c478bd9Sstevel@tonic-gate 		 */
9017c478bd9Sstevel@tonic-gate 		pcf8584_put_s1(i2c, S1_STOP);
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 		return (I2C_FAILURE);
9047c478bd9Sstevel@tonic-gate 	}
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	return (I2C_SUCCESS);
9077c478bd9Sstevel@tonic-gate }
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate static void
9107c478bd9Sstevel@tonic-gate pcf8584_monitor_mode(pcf8584_t *i2c)
9117c478bd9Sstevel@tonic-gate {
9127c478bd9Sstevel@tonic-gate 	pcf8584_put_s1(i2c, S1_PIN);
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	pcf8584_put_s0(i2c, MONITOR_ADDRESS);
9157c478bd9Sstevel@tonic-gate }
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate static int
9187c478bd9Sstevel@tonic-gate pcf8584_initchild(dev_info_t *cdip)
9197c478bd9Sstevel@tonic-gate {
9207c478bd9Sstevel@tonic-gate 	int32_t cell_size;
9217c478bd9Sstevel@tonic-gate 	int len;
9227c478bd9Sstevel@tonic-gate 	int32_t regs[2];
9237c478bd9Sstevel@tonic-gate 	int err;
9247c478bd9Sstevel@tonic-gate 	pcf8584_ppvt_t *ppvt;
9257c478bd9Sstevel@tonic-gate 	char name[30];
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	PCF8584_DDB(pcf8584_print(PRT_INIT, "pcf8584_initchild enter: %s\n",
9287c478bd9Sstevel@tonic-gate 		ddi_node_name(cdip)));
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	ppvt = kmem_alloc(sizeof (pcf8584_ppvt_t), KM_SLEEP);
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	len = sizeof (cell_size);
9337c478bd9Sstevel@tonic-gate 	err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
9347c478bd9Sstevel@tonic-gate 		DDI_PROP_CANSLEEP, "#address-cells",
9357c478bd9Sstevel@tonic-gate 		(caddr_t)&cell_size, &len);
9367c478bd9Sstevel@tonic-gate 	if (err != DDI_PROP_SUCCESS || len != sizeof (cell_size)) {
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9397c478bd9Sstevel@tonic-gate 	}
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	len = sizeof (regs);
9427c478bd9Sstevel@tonic-gate 	err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
9437c478bd9Sstevel@tonic-gate 		DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
9447c478bd9Sstevel@tonic-gate 		"reg", (caddr_t)regs, &len);
9457c478bd9Sstevel@tonic-gate 	if (err != DDI_PROP_SUCCESS ||
9467c478bd9Sstevel@tonic-gate 		len != (cell_size * sizeof (int32_t))) {
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9497c478bd9Sstevel@tonic-gate 	}
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 	if (cell_size == 1) {
9527c478bd9Sstevel@tonic-gate 		ppvt->pcf8584_ppvt_addr = regs[0];
9537c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%x", regs[0]);
9547c478bd9Sstevel@tonic-gate 	} else if (cell_size == 2) {
9557c478bd9Sstevel@tonic-gate 		ppvt->pcf8584_ppvt_bus = regs[0];
9567c478bd9Sstevel@tonic-gate 		ppvt->pcf8584_ppvt_addr = regs[1];
9577c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%x,%x", regs[0], regs[1]);
9587c478bd9Sstevel@tonic-gate 	} else {
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9617c478bd9Sstevel@tonic-gate 	}
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	ddi_set_parent_data(cdip, ppvt);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	ddi_set_name_addr(cdip, name);
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate 	PCF8584_DDB(pcf8584_print(PRT_INIT,
9687c478bd9Sstevel@tonic-gate 	    "pcf8584_initchild SUCCESS: %s\n", ddi_node_name(cdip)));
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
9717c478bd9Sstevel@tonic-gate }
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate static void
9747c478bd9Sstevel@tonic-gate pcf8584_uninitchild(dev_info_t *cdip)
9757c478bd9Sstevel@tonic-gate {
9767c478bd9Sstevel@tonic-gate 	pcf8584_ppvt_t *ppvt;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 	ppvt = ddi_get_parent_data(cdip);
9797c478bd9Sstevel@tonic-gate 	kmem_free(ppvt, sizeof (pcf8584_ppvt_t));
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate 	ddi_set_parent_data(cdip, NULL);
9827c478bd9Sstevel@tonic-gate 	ddi_set_name_addr(cdip, NULL);
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	PCF8584_DDB(pcf8584_print(PRT_INIT, "i2c_uninitchild: %s\n",
9857c478bd9Sstevel@tonic-gate 	    ddi_node_name(cdip)));
9867c478bd9Sstevel@tonic-gate }
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate static void
9897c478bd9Sstevel@tonic-gate pcf8584_init(pcf8584_t *i2c)
9907c478bd9Sstevel@tonic-gate {
9917c478bd9Sstevel@tonic-gate 	uint8_t clk_div = 0x1C;
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 	pcf8584_put_s1(i2c, S1_PIN);
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	pcf8584_put_s0(i2c, S0_OWN);
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	pcf8584_put_s1(i2c, S1_PIN | S1_ES1);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	/*
10007c478bd9Sstevel@tonic-gate 	 * The default case is to set the clock divisor to the least common
10017c478bd9Sstevel@tonic-gate 	 * denominator to avoid over clocking the I2C bus.  Assume that
10027c478bd9Sstevel@tonic-gate 	 * BBC based systems are using the Safari clock as input, so select
10037c478bd9Sstevel@tonic-gate 	 * the clk divisor based on it.
10047c478bd9Sstevel@tonic-gate 	 */
1005afd7fd7bSosaeed 	if (i2c->pcf8584_impl_type == BBC) {
10067c478bd9Sstevel@tonic-gate 		dev_info_t *root_node;
10077c478bd9Sstevel@tonic-gate 		int clock_freq;
10087c478bd9Sstevel@tonic-gate 		root_node = ddi_root_node();
10097c478bd9Sstevel@tonic-gate 		clock_freq = ddi_prop_get_int(DDI_DEV_T_ANY, root_node,
10107c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "clock-frequency", 0);
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		if (clock_freq < 105000000) {
10137c478bd9Sstevel@tonic-gate 			clk_div = 0x00;
10147c478bd9Sstevel@tonic-gate 		} else if (clock_freq < 160000000) {
10157c478bd9Sstevel@tonic-gate 			clk_div = 0x10;
10167c478bd9Sstevel@tonic-gate 		} else {
10177c478bd9Sstevel@tonic-gate 			clk_div = 0x1C;
10187c478bd9Sstevel@tonic-gate 		}
10197c478bd9Sstevel@tonic-gate 	}
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 	/* set I2C clock speed */
10227c478bd9Sstevel@tonic-gate 	pcf8584_put_s0(i2c, clk_div);
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	pcf8584_put_s1(i2c, S1_PIN | S1_ESO | S1_ACK);
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	/*
10277c478bd9Sstevel@tonic-gate 	 * Multi-Master: Wait for a period of time equal to the
10287c478bd9Sstevel@tonic-gate 	 * longest I2C message.  This accounts for the case
10297c478bd9Sstevel@tonic-gate 	 * where multiple controllers and, if this particular one
10307c478bd9Sstevel@tonic-gate 	 * is "lagging", misses the BB(bus busy) condition.
10317c478bd9Sstevel@tonic-gate 	 * We wait 200 ms since the longest transaction at this time
10327c478bd9Sstevel@tonic-gate 	 * on the i2c bus is a 256 byte read from the seprom which takes
10337c478bd9Sstevel@tonic-gate 	 * about 75 ms. Some additional buffer does no harm to the driver.
10347c478bd9Sstevel@tonic-gate 	 */
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(PCF8584_INIT_WAIT));
10377c478bd9Sstevel@tonic-gate }
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate /*
10407c478bd9Sstevel@tonic-gate  * pcf8584_setup_regs() is called to map in registers specific to
10417c478bd9Sstevel@tonic-gate  * the pcf8584.
10427c478bd9Sstevel@tonic-gate  */
10437c478bd9Sstevel@tonic-gate static int
10447c478bd9Sstevel@tonic-gate pcf8584_setup_regs(dev_info_t *dip, pcf8584_t *i2c)
10457c478bd9Sstevel@tonic-gate {
10467c478bd9Sstevel@tonic-gate 	int nregs;
10477c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
10487c478bd9Sstevel@tonic-gate 	caddr_t reg_base;
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
10517c478bd9Sstevel@tonic-gate 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
10527c478bd9Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) {
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10577c478bd9Sstevel@tonic-gate 	}
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(dip, 0,
10607c478bd9Sstevel@tonic-gate 	    (caddr_t *)&reg_base, 0, 0, &attr,
10617c478bd9Sstevel@tonic-gate 	    &i2c->pcf8584_rhandle) != DDI_SUCCESS) {
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10647c478bd9Sstevel@tonic-gate 	}
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	/*
10677c478bd9Sstevel@tonic-gate 	 * If i2c controller is on BBC, then s1 comes before s0.
10687c478bd9Sstevel@tonic-gate 	 */
1069afd7fd7bSosaeed 	if (i2c->pcf8584_impl_type == BBC) {
10707c478bd9Sstevel@tonic-gate 		i2c->pcf8584_regs.pcf8584_regs_s0 =
10717c478bd9Sstevel@tonic-gate 		    (uint8_t *)&reg_base[1];
10727c478bd9Sstevel@tonic-gate 		i2c->pcf8584_regs.pcf8584_regs_s1 =
10737c478bd9Sstevel@tonic-gate 		    (uint8_t *)&reg_base[0];
10747c478bd9Sstevel@tonic-gate 	} else {
10757c478bd9Sstevel@tonic-gate 		i2c->pcf8584_regs.pcf8584_regs_s0 =
10767c478bd9Sstevel@tonic-gate 		    (uint8_t *)&reg_base[0];
10777c478bd9Sstevel@tonic-gate 		i2c->pcf8584_regs.pcf8584_regs_s1 =
10787c478bd9Sstevel@tonic-gate 		    (uint8_t *)&reg_base[1];
10797c478bd9Sstevel@tonic-gate 	}
10807c478bd9Sstevel@tonic-gate 
10817c478bd9Sstevel@tonic-gate 	if (nregs > 1) {
10827c478bd9Sstevel@tonic-gate 		if (ddi_regs_map_setup(dip,
10837c478bd9Sstevel@tonic-gate 			1, (caddr_t *)&i2c->pcf8584_b_reg,
10847c478bd9Sstevel@tonic-gate 			0, 0, &attr, &i2c->pcf8584_b_rhandle) !=
10857c478bd9Sstevel@tonic-gate 			DDI_SUCCESS) {
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
10887c478bd9Sstevel@tonic-gate 		}
10897c478bd9Sstevel@tonic-gate 	}
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
10927c478bd9Sstevel@tonic-gate }
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate /*
10957c478bd9Sstevel@tonic-gate  * pcf8584_free_regs() frees any registers previously
10967c478bd9Sstevel@tonic-gate  * allocated.
10977c478bd9Sstevel@tonic-gate  */
10987c478bd9Sstevel@tonic-gate static void
10997c478bd9Sstevel@tonic-gate pcf8584_free_regs(pcf8584_t *i2c)
11007c478bd9Sstevel@tonic-gate {
11017c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_regs.pcf8584_regs_s0 != NULL) {
11027c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&i2c->pcf8584_rhandle);
11037c478bd9Sstevel@tonic-gate 	}
11047c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_b_reg != NULL) {
11057c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&i2c->pcf8584_b_rhandle);
11067c478bd9Sstevel@tonic-gate 	}
11077c478bd9Sstevel@tonic-gate }
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate static void
11107c478bd9Sstevel@tonic-gate pcf8584_reportdev(dev_info_t *dip, dev_info_t *rdip)
11117c478bd9Sstevel@tonic-gate {
11127c478bd9Sstevel@tonic-gate 	pcf8584_ppvt_t *ppvt;
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	ppvt = ddi_get_parent_data(rdip);
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	cmn_err(CE_CONT, "?%s%d at %s%d: addr 0x%x",
11177c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
11187c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
11197c478bd9Sstevel@tonic-gate 	    ppvt->pcf8584_ppvt_addr);
11207c478bd9Sstevel@tonic-gate }
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate /*
11237c478bd9Sstevel@tonic-gate  * i2_nexus_dip_to_addr() takes a dip and returns an I2C address.
11247c478bd9Sstevel@tonic-gate  */
11257c478bd9Sstevel@tonic-gate static int
11267c478bd9Sstevel@tonic-gate pcf8584_dip_to_addr(dev_info_t *dip)
11277c478bd9Sstevel@tonic-gate {
11287c478bd9Sstevel@tonic-gate 	pcf8584_ppvt_t *ppvt;
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	ppvt = ddi_get_parent_data(dip);
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	return (ppvt->pcf8584_ppvt_addr);
11337c478bd9Sstevel@tonic-gate }
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate /*
11367c478bd9Sstevel@tonic-gate  * pcf8584_intr() is the interrupt service routine registered during
11377c478bd9Sstevel@tonic-gate  * attach, and remains registered even if the driver is in POLLED mode.  So if
11387c478bd9Sstevel@tonic-gate  * this is called from POLLED mode, it needs to return without doing
11397c478bd9Sstevel@tonic-gate  * any work to prevent the I2C bus from entering an unknown state.
11407c478bd9Sstevel@tonic-gate  */
11417c478bd9Sstevel@tonic-gate static uint_t
11427c478bd9Sstevel@tonic-gate pcf8584_intr(caddr_t arg)
11437c478bd9Sstevel@tonic-gate {
11447c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c = (pcf8584_t *)arg;
11457c478bd9Sstevel@tonic-gate 	uint8_t s1;
11467c478bd9Sstevel@tonic-gate 
1147afd7fd7bSosaeed 	ASSERT(i2c->pcf8584_mode != PCF8584_POLL_MODE);
11487c478bd9Sstevel@tonic-gate 	PCF8584_DDB(pcf8584_print(PRT_INTR, "pcf8584_intr: enter\n"));
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	/*
11537c478bd9Sstevel@tonic-gate 	 * It is necessary to check both whether the hardware is interrupting
11547c478bd9Sstevel@tonic-gate 	 * and that there is a current transaction for the bus in progress.
11557c478bd9Sstevel@tonic-gate 	 * Checking just one but not the other will lead to a panic on xcal
11567c478bd9Sstevel@tonic-gate 	 * since both controllers share the same ino, and also because OBP
11577c478bd9Sstevel@tonic-gate 	 * shares a controller with the kernel even while the kernel is running.
11587c478bd9Sstevel@tonic-gate 	 */
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_cur_tran == NULL) {
11617c478bd9Sstevel@tonic-gate 		mutex_exit(&i2c->pcf8584_imutex);
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
11647c478bd9Sstevel@tonic-gate 	}
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	s1 = pcf8584_get_s1(i2c);
11687c478bd9Sstevel@tonic-gate 	if (s1 & S1_PIN) {
11697c478bd9Sstevel@tonic-gate 		mutex_exit(&i2c->pcf8584_imutex);
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
11727c478bd9Sstevel@tonic-gate 	}
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate 	if (pcf8584_process(i2c, s1) == I2C_COMPLETE) {
11757c478bd9Sstevel@tonic-gate 		i2c->pcf8584_tran_state = TRAN_STATE_NULL;
11767c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_status = PCF8584_TRANSFER_OVER;
11777c478bd9Sstevel@tonic-gate 		cv_signal(&i2c->pcf8584_icv);
11787c478bd9Sstevel@tonic-gate 	} else
11797c478bd9Sstevel@tonic-gate 		i2c->pcf8584_cur_status = PCF8584_TRANSFER_ON;
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
11847c478bd9Sstevel@tonic-gate }
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate /*
11877c478bd9Sstevel@tonic-gate  * Interrupt occurs after a byte is transmitted or received, indicating
11887c478bd9Sstevel@tonic-gate  * the device is ready to be serviced.
11897c478bd9Sstevel@tonic-gate  */
11907c478bd9Sstevel@tonic-gate static int
11917c478bd9Sstevel@tonic-gate pcf8584_process(pcf8584_t *i2c, uint8_t s1)
11927c478bd9Sstevel@tonic-gate {
11937c478bd9Sstevel@tonic-gate 	i2c_transfer_t *tp = i2c->pcf8584_cur_tran;
11947c478bd9Sstevel@tonic-gate 	int addr = pcf8584_dip_to_addr(i2c->pcf8584_cur_dip);
11957c478bd9Sstevel@tonic-gate 	int dummy_read;
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	ASSERT(i2c->pcf8584_tran_state != TRAN_STATE_NULL);
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	switch (i2c->pcf8584_tran_state) {
12007c478bd9Sstevel@tonic-gate 	case TRAN_STATE_DUMMY_DATA:
12017c478bd9Sstevel@tonic-gate 		PCF8584_DDB(pcf8584_print(PRT_TRAN,
12027c478bd9Sstevel@tonic-gate 		    "TRAN_STATE_DUMMY DATA: write dummy %x\n", DUMMY_DATA));
12037c478bd9Sstevel@tonic-gate 		if (pcf8584_error(s1, I2C_RD, i2c) != I2C_SUCCESS) {
12047c478bd9Sstevel@tonic-gate 			tp->i2c_result = I2C_FAILURE;
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
12077c478bd9Sstevel@tonic-gate 		}
12087c478bd9Sstevel@tonic-gate 		i2c->pcf8584_tran_state = TRAN_STATE_START;
12097c478bd9Sstevel@tonic-gate 		pcf8584_put_s0(i2c, DUMMY_DATA);
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 		return (I2C_PENDING);
12127c478bd9Sstevel@tonic-gate 	case TRAN_STATE_START:
12137c478bd9Sstevel@tonic-gate 		if (pcf8584_error(s1, I2C_RD, i2c) != I2C_SUCCESS) {
12147c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
12157c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_START failure\n"));
12167c478bd9Sstevel@tonic-gate 			tp->i2c_result = I2C_FAILURE;
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
12197c478bd9Sstevel@tonic-gate 		}
12207c478bd9Sstevel@tonic-gate 		i2c->pcf8584_tran_state =
12217c478bd9Sstevel@tonic-gate 			pcf8584_type_to_state(tp->i2c_flags);
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 		/* Set read bit if this is a read transaction */
12247c478bd9Sstevel@tonic-gate 		if (tp->i2c_flags == I2C_RD) {
12257c478bd9Sstevel@tonic-gate 			addr |= I2C_READ;
12267c478bd9Sstevel@tonic-gate 		}
1227afd7fd7bSosaeed 		if (i2c->pcf8584_mode == PCF8584_POLL_MODE)
1228afd7fd7bSosaeed 			pcf8584_put_s1(i2c, S1_START2);
1229afd7fd7bSosaeed 		else
1230afd7fd7bSosaeed 			pcf8584_put_s1(i2c, S1_START2 | S1_ENI);
12317c478bd9Sstevel@tonic-gate 		pcf8584_put_s0(i2c, addr);
12327c478bd9Sstevel@tonic-gate 		PCF8584_DDB(pcf8584_print(PRT_TRAN,
12337c478bd9Sstevel@tonic-gate 		    "TRAN_STATE_START: write addr: %x\n", addr));
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate 		return (I2C_PENDING);
12367c478bd9Sstevel@tonic-gate 	case TRAN_STATE_WR:
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 		if (pcf8584_error(s1, I2C_WR, i2c) != I2C_SUCCESS) {
12397c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
12407c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_WR failure\n"));
12417c478bd9Sstevel@tonic-gate 			tp->i2c_result = I2C_FAILURE;
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
12447c478bd9Sstevel@tonic-gate 		}
12457c478bd9Sstevel@tonic-gate 		/* check to see if at end of buffer */
12467c478bd9Sstevel@tonic-gate 		if (tp->i2c_w_resid == 0) {
12477c478bd9Sstevel@tonic-gate 			pcf8584_put_s1(i2c, S1_STOP);
12487c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
12497c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_WR: write STOP\n"));
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
12527c478bd9Sstevel@tonic-gate 		}
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate 		pcf8584_put_s0(i2c, tp->i2c_wbuf[tp->i2c_wlen -
12557c478bd9Sstevel@tonic-gate 		    tp->i2c_w_resid--]);
12567c478bd9Sstevel@tonic-gate 		PCF8584_DDB(pcf8584_print(PRT_TRAN,
12577c478bd9Sstevel@tonic-gate 		    "TRAN_STATE_WR:  write data %x\n",
12587c478bd9Sstevel@tonic-gate 		    tp->i2c_wbuf[tp->i2c_wlen - (tp->i2c_w_resid + 1)]));
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 		return (I2C_PENDING);
12617c478bd9Sstevel@tonic-gate 	case TRAN_STATE_DUMMY_RD:
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 		if (pcf8584_error(s1, I2C_WR, i2c) != I2C_SUCCESS) {
12647c478bd9Sstevel@tonic-gate 			tp->i2c_result = I2C_FAILURE;
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
12677c478bd9Sstevel@tonic-gate 		}
12687c478bd9Sstevel@tonic-gate 		/*
12697c478bd9Sstevel@tonic-gate 		 * The first read is always a dummy read, because reading S0
12707c478bd9Sstevel@tonic-gate 		 * is what starts bit shifting and ACK on the I2c bus.
12717c478bd9Sstevel@tonic-gate 		 * This byte is accessed during the next read, which starts
12727c478bd9Sstevel@tonic-gate 		 * another 8 bit bus shift.
12737c478bd9Sstevel@tonic-gate 		 *
12747c478bd9Sstevel@tonic-gate 		 * special case for 1 byte reads:  Clear the ACK bit
12757c478bd9Sstevel@tonic-gate 		 * here since this read causes the last and only byte
12767c478bd9Sstevel@tonic-gate 		 * to be sent on the I2C bus.
12777c478bd9Sstevel@tonic-gate 		 */
12787c478bd9Sstevel@tonic-gate 		if (tp->i2c_r_resid  == 1) {
1279afd7fd7bSosaeed 			if (i2c->pcf8584_mode == PCF8584_POLL_MODE)
1280afd7fd7bSosaeed 				pcf8584_put_s1(i2c, S1_ESO);
1281afd7fd7bSosaeed 			else
1282afd7fd7bSosaeed 				pcf8584_put_s1(i2c, S1_ESO | S1_ENI);
12837c478bd9Sstevel@tonic-gate 		}
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 		/*
12867c478bd9Sstevel@tonic-gate 		 * dummy read
12877c478bd9Sstevel@tonic-gate 		 */
12887c478bd9Sstevel@tonic-gate 		dummy_read = pcf8584_get_s0(i2c);
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 		i2c->pcf8584_tran_state = TRAN_STATE_RD;
12917c478bd9Sstevel@tonic-gate 		PCF8584_DDB(pcf8584_print(PRT_TRAN,
12927c478bd9Sstevel@tonic-gate 		    "TRAN_STATE_DUMMY_RD: read dummy %d\n", dummy_read));
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 		return (I2C_PENDING);
12957c478bd9Sstevel@tonic-gate 	case TRAN_STATE_RD:
12967c478bd9Sstevel@tonic-gate 		if (pcf8584_error(s1, I2C_RD, i2c) != I2C_SUCCESS) {
12977c478bd9Sstevel@tonic-gate 			tp->i2c_result = I2C_FAILURE;
12987c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
12997c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_RD failure\n"));
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
13027c478bd9Sstevel@tonic-gate 		}
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 		/*
13057c478bd9Sstevel@tonic-gate 		 * If resid == 1, the last byte has already been shifted into
13067c478bd9Sstevel@tonic-gate 		 * the accumulator.  Send the stop bit.  This also prevents the
13077c478bd9Sstevel@tonic-gate 		 * last S0 read from shifting in another byte from the I2C bus.
13087c478bd9Sstevel@tonic-gate 		 */
13097c478bd9Sstevel@tonic-gate 		if (tp->i2c_r_resid  == 1) {
13107c478bd9Sstevel@tonic-gate 			pcf8584_put_s1(i2c, S1_STOP);
13117c478bd9Sstevel@tonic-gate 		}
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 		/*
13147c478bd9Sstevel@tonic-gate 		 * If resid == 2, then the next read will cause the I2C bus to
13157c478bd9Sstevel@tonic-gate 		 * start shifting in the last byte on the I2C bus, which we
13167c478bd9Sstevel@tonic-gate 		 * don't want to be ACK'd, so clear the ACK bit.
13177c478bd9Sstevel@tonic-gate 		 */
13187c478bd9Sstevel@tonic-gate 		if (tp->i2c_r_resid  == 2) {
1319afd7fd7bSosaeed 			if (i2c->pcf8584_mode == PCF8584_POLL_MODE)
1320afd7fd7bSosaeed 				pcf8584_put_s1(i2c, S1_ESO);
1321afd7fd7bSosaeed 			else
1322afd7fd7bSosaeed 				pcf8584_put_s1(i2c, S1_ESO | S1_ENI);
13237c478bd9Sstevel@tonic-gate 		}
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 		tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] =
13267c478bd9Sstevel@tonic-gate 			pcf8584_get_s0(i2c);
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 		PCF8584_DDB(pcf8584_print(PRT_TRAN,
13297c478bd9Sstevel@tonic-gate 		    "TRAN_STATE_RD: returning. i2c_rlen = %d "
13307c478bd9Sstevel@tonic-gate 		    "i2c_r_resid = %d,  data =%x\n", tp->i2c_rlen,
13317c478bd9Sstevel@tonic-gate 		    tp->i2c_r_resid, tp->i2c_rbuf[tp->i2c_rlen -
13327c478bd9Sstevel@tonic-gate 		    tp->i2c_r_resid]));
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 		if (--tp->i2c_r_resid == 0) {
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
13377c478bd9Sstevel@tonic-gate 		}
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 		return (I2C_PENDING);
13407c478bd9Sstevel@tonic-gate 	case TRAN_STATE_WR_RD:
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 		if (pcf8584_error(s1, I2C_WR, i2c) != I2C_SUCCESS) {
13437c478bd9Sstevel@tonic-gate 			tp->i2c_result = I2C_FAILURE;
13447c478bd9Sstevel@tonic-gate 
13457c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
13467c478bd9Sstevel@tonic-gate 		}
13477c478bd9Sstevel@tonic-gate 		if ((s1 & S1_LRB)) {
13487c478bd9Sstevel@tonic-gate 			pcf8584_put_s1(i2c, S1_STOP);
13497c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
13507c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_WR_RD sending STOP\n"));
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate 			return (I2C_COMPLETE);
13537c478bd9Sstevel@tonic-gate 		}
13547c478bd9Sstevel@tonic-gate 		if (tp->i2c_w_resid != 0) {
13557c478bd9Sstevel@tonic-gate 			pcf8584_put_s0(i2c, tp->i2c_wbuf[tp->i2c_wlen -
13567c478bd9Sstevel@tonic-gate 				tp->i2c_w_resid--]);
13577c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
13587c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_WR_RD: write data %x\n",
13597c478bd9Sstevel@tonic-gate 			    tp->i2c_wbuf[tp->i2c_wlen -
13607c478bd9Sstevel@tonic-gate 			    (tp->i2c_w_resid + 1)]));
13617c478bd9Sstevel@tonic-gate 		} else {
1362afd7fd7bSosaeed 			if (i2c->pcf8584_mode == PCF8584_POLL_MODE)
1363afd7fd7bSosaeed 				pcf8584_put_s1(i2c, S1_START2);
1364afd7fd7bSosaeed 			else
1365afd7fd7bSosaeed 				pcf8584_put_s1(i2c, S1_START2 | S1_ENI);
13667c478bd9Sstevel@tonic-gate 			pcf8584_put_s0(i2c, addr | I2C_READ);
13677c478bd9Sstevel@tonic-gate 			i2c->pcf8584_tran_state =
13687c478bd9Sstevel@tonic-gate 				TRAN_STATE_DUMMY_RD;
13697c478bd9Sstevel@tonic-gate 			PCF8584_DDB(pcf8584_print(PRT_TRAN,
13707c478bd9Sstevel@tonic-gate 			    "TRAN_STATE_WR_RD: write addr "
13717c478bd9Sstevel@tonic-gate 			    "%x\n", addr | I2C_READ));
13727c478bd9Sstevel@tonic-gate 		}
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 		return (I2C_PENDING);
13757c478bd9Sstevel@tonic-gate 	default:
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 		return (I2C_COMPLETE);
13787c478bd9Sstevel@tonic-gate 	}
13797c478bd9Sstevel@tonic-gate }
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate /*
13827c478bd9Sstevel@tonic-gate  * pcf8584_transfer() is the function that is registered with
13837c478bd9Sstevel@tonic-gate  * I2C services to be called from pcf8584_transfer() for each transfer.
13847c478bd9Sstevel@tonic-gate  *
13857c478bd9Sstevel@tonic-gate  * This function starts the transfer, and then waits for the
13867c478bd9Sstevel@tonic-gate  * interrupt or polled thread to signal that the transfer has
13877c478bd9Sstevel@tonic-gate  * completed.
13887c478bd9Sstevel@tonic-gate  */
13897c478bd9Sstevel@tonic-gate int
13907c478bd9Sstevel@tonic-gate pcf8584_transfer(dev_info_t *dip, i2c_transfer_t *tp)
13917c478bd9Sstevel@tonic-gate {
13927c478bd9Sstevel@tonic-gate 	pcf8584_t *i2c;
13937c478bd9Sstevel@tonic-gate 	int saved_mode, took_over = 0;
13947c478bd9Sstevel@tonic-gate 	kcondvar_t *waiter = NULL;
13957c478bd9Sstevel@tonic-gate 	extern int do_polled_io;
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 	i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state,
13987c478bd9Sstevel@tonic-gate 		ddi_get_instance(ddi_get_parent(dip)));
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	tp->i2c_r_resid = tp->i2c_rlen;
14017c478bd9Sstevel@tonic-gate 	tp->i2c_w_resid = tp->i2c_wlen;
14027c478bd9Sstevel@tonic-gate 	tp->i2c_result = I2C_SUCCESS;
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate begin:
14057c478bd9Sstevel@tonic-gate 	/*
14067c478bd9Sstevel@tonic-gate 	 * If we're explicitly asked to do polled io (or if we are panic'ing),
14077c478bd9Sstevel@tonic-gate 	 * we need to usurp ownership of the I2C bus, bypassing any other
14087c478bd9Sstevel@tonic-gate 	 * waiters.
14097c478bd9Sstevel@tonic-gate 	 */
1410afd7fd7bSosaeed 	if (do_polled_io || ddi_in_panic()) {
14117c478bd9Sstevel@tonic-gate 		pcf8584_take_over(i2c, dip, tp, &waiter, &saved_mode);
14127c478bd9Sstevel@tonic-gate 		took_over = 1;
14137c478bd9Sstevel@tonic-gate 	} else {
14147c478bd9Sstevel@tonic-gate 		pcf8584_acquire(i2c, dip, tp, B_FALSE);
14157c478bd9Sstevel@tonic-gate 		mutex_enter(&i2c->pcf8584_imutex);
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 		/*
14187c478bd9Sstevel@tonic-gate 		 * See if someone else had intruded and taken over the bus
14197c478bd9Sstevel@tonic-gate 		 * between the 'pcf8584_acquire' and 'mutex_enter' above.
14207c478bd9Sstevel@tonic-gate 		 * If so, we'll have to start all over again.
14217c478bd9Sstevel@tonic-gate 		 */
14227c478bd9Sstevel@tonic-gate 		if (i2c->pcf8584_cur_tran != tp) {
14237c478bd9Sstevel@tonic-gate 			mutex_exit(&i2c->pcf8584_imutex);
14247c478bd9Sstevel@tonic-gate 			goto begin;
14257c478bd9Sstevel@tonic-gate 		}
14267c478bd9Sstevel@tonic-gate 	}
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	if (pcf8584_bbn_ready(i2c) != I2C_SUCCESS) {
14297c478bd9Sstevel@tonic-gate 		if (took_over)
14307c478bd9Sstevel@tonic-gate 			pcf8584_give_up(i2c, waiter, saved_mode);
14317c478bd9Sstevel@tonic-gate 		else {
14327c478bd9Sstevel@tonic-gate 			mutex_exit(&i2c->pcf8584_imutex);
14337c478bd9Sstevel@tonic-gate 			pcf8584_release(i2c, B_FALSE);
14347c478bd9Sstevel@tonic-gate 		}
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 		return (tp->i2c_result = I2C_FAILURE);
14377c478bd9Sstevel@tonic-gate 	}
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	/*
14407c478bd9Sstevel@tonic-gate 	 * Bus selection must be followed by pcf8584_bbn_ready(),
14417c478bd9Sstevel@tonic-gate 	 * otherwise the bus can be switched before the stop
14427c478bd9Sstevel@tonic-gate 	 * bit is written out, causing the stop bit to get
14437c478bd9Sstevel@tonic-gate 	 * sent to the wrong (new) bus.  This causes the
14447c478bd9Sstevel@tonic-gate 	 * previous bus to permanently hang waiting for the
14457c478bd9Sstevel@tonic-gate 	 * stop bit.
14467c478bd9Sstevel@tonic-gate 	 */
14477c478bd9Sstevel@tonic-gate 	pcf8584_select_bus(i2c);
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 	i2c->pcf8584_tran_state = TRAN_STATE_DUMMY_DATA;
14507c478bd9Sstevel@tonic-gate 	pcf8584_put_s0(i2c, DUMMY_ADDR);
14517c478bd9Sstevel@tonic-gate 	PCF8584_DDB(pcf8584_print(PRT_TRAN,
14527c478bd9Sstevel@tonic-gate 	    "FIRST WRITE DUMMY ADDR: write %x\n", DUMMY_ADDR));
1453afd7fd7bSosaeed 	if (i2c->pcf8584_mode ==  PCF8584_POLL_MODE)
1454afd7fd7bSosaeed 		pcf8584_put_s1(i2c, S1_START);
1455afd7fd7bSosaeed 	else
1456afd7fd7bSosaeed 		pcf8584_put_s1(i2c, S1_START | S1_ENI);
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	/*
14597c478bd9Sstevel@tonic-gate 	 * Update transfer status so any polled i/o request coming in
14607c478bd9Sstevel@tonic-gate 	 * after this will complete this transfer for us, before issuing
14617c478bd9Sstevel@tonic-gate 	 * its own.
14627c478bd9Sstevel@tonic-gate 	 */
14637c478bd9Sstevel@tonic-gate 	i2c->pcf8584_cur_status = PCF8584_TRANSFER_ON;
14647c478bd9Sstevel@tonic-gate 
14657c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_mode ==  PCF8584_POLL_MODE)
14667c478bd9Sstevel@tonic-gate 		pcf8584_do_polled_io(i2c);
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	if (took_over)
14697c478bd9Sstevel@tonic-gate 		pcf8584_give_up(i2c, waiter, saved_mode);
14707c478bd9Sstevel@tonic-gate 	else {
14717c478bd9Sstevel@tonic-gate 		if (i2c->pcf8584_mode != PCF8584_POLL_MODE)
14727c478bd9Sstevel@tonic-gate 			cv_wait(&i2c->pcf8584_icv, &i2c->pcf8584_imutex);
14737c478bd9Sstevel@tonic-gate 		mutex_exit(&i2c->pcf8584_imutex);
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 		/*
14767c478bd9Sstevel@tonic-gate 		 * Release the I2C bus only if we still own it. If we don't
14777c478bd9Sstevel@tonic-gate 		 * own it (someone usurped it from us while we were waiting),
14787c478bd9Sstevel@tonic-gate 		 * we still need to drop the lock that serializes access to
14797c478bd9Sstevel@tonic-gate 		 * the pcf8584 controller on systems where OBP shares the
14807c478bd9Sstevel@tonic-gate 		 * controller with the OS.
14817c478bd9Sstevel@tonic-gate 		 */
14827c478bd9Sstevel@tonic-gate 		if (i2c->pcf8584_cur_tran == tp)
14837c478bd9Sstevel@tonic-gate 			pcf8584_release(i2c, B_FALSE);
14847c478bd9Sstevel@tonic-gate 		else if (&plat_shared_i2c_exit && dip)
14857c478bd9Sstevel@tonic-gate 			plat_shared_i2c_exit(i2c->pcf8584_dip);
14867c478bd9Sstevel@tonic-gate 	}
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	return (tp->i2c_result);
14897c478bd9Sstevel@tonic-gate }
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate static void
14927c478bd9Sstevel@tonic-gate pcf8584_do_polled_io(pcf8584_t *i2c)
14937c478bd9Sstevel@tonic-gate {
14947c478bd9Sstevel@tonic-gate 	int completed = I2C_PENDING;
14957c478bd9Sstevel@tonic-gate 	uint8_t s1;
14967c478bd9Sstevel@tonic-gate 
14977c478bd9Sstevel@tonic-gate 	while (completed != I2C_COMPLETE) {
14987c478bd9Sstevel@tonic-gate 		s1 = pcf8584_get_s1(i2c);
14997c478bd9Sstevel@tonic-gate 		if (!(s1 & S1_PIN)) {
15007c478bd9Sstevel@tonic-gate 			ASSERT(i2c->pcf8584_cur_tran);
15017c478bd9Sstevel@tonic-gate 			completed = pcf8584_process(i2c, s1);
15027c478bd9Sstevel@tonic-gate 		}
15037c478bd9Sstevel@tonic-gate 		drv_usecwait(1);
15047c478bd9Sstevel@tonic-gate 	}
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 	i2c->pcf8584_cur_status = PCF8584_TRANSFER_OVER;
15077c478bd9Sstevel@tonic-gate }
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate /*
15107c478bd9Sstevel@tonic-gate  * pcf8584_take_over() grabs the I2C bus and other resources by force and
15117c478bd9Sstevel@tonic-gate  * flushes any pending transaction. This is called if a polled i/o
15127c478bd9Sstevel@tonic-gate  * request comes in.
15137c478bd9Sstevel@tonic-gate  */
15147c478bd9Sstevel@tonic-gate static void
15157c478bd9Sstevel@tonic-gate pcf8584_take_over(pcf8584_t *i2c, dev_info_t *dip, i2c_transfer_t *tp,
15167c478bd9Sstevel@tonic-gate     kcondvar_t **waiter, int *saved_mode)
15177c478bd9Sstevel@tonic-gate {
15187c478bd9Sstevel@tonic-gate 	mutex_enter(&i2c->pcf8584_imutex);
1519afd7fd7bSosaeed 	*saved_mode = i2c->pcf8584_mode;
1520afd7fd7bSosaeed 	i2c->pcf8584_mode = PCF8584_POLL_MODE;
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 	/*
15237c478bd9Sstevel@tonic-gate 	 * We need to flush out any currently pending transaction before
15247c478bd9Sstevel@tonic-gate 	 * issuing ours.
15257c478bd9Sstevel@tonic-gate 	 */
15267c478bd9Sstevel@tonic-gate 	if (i2c->pcf8584_busy) {
15277c478bd9Sstevel@tonic-gate 		if (i2c->pcf8584_cur_tran &&
15287c478bd9Sstevel@tonic-gate 		    i2c->pcf8584_cur_status == PCF8584_TRANSFER_ON) {
15297c478bd9Sstevel@tonic-gate 			pcf8584_do_polled_io(i2c);
15307c478bd9Sstevel@tonic-gate 			*waiter = &i2c->pcf8584_icv;
15317c478bd9Sstevel@tonic-gate 		}
15327c478bd9Sstevel@tonic-gate 	}
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 	/*
15357c478bd9Sstevel@tonic-gate 	 * Since pcf8584_acquire() is by default a good citizen that
15367c478bd9Sstevel@tonic-gate 	 * will wait its turn to acquire the I2C bus, we need to set
15377c478bd9Sstevel@tonic-gate 	 * the 'force' flag on.
15387c478bd9Sstevel@tonic-gate 	 */
15397c478bd9Sstevel@tonic-gate 	pcf8584_acquire(i2c, dip, tp, B_TRUE);
15407c478bd9Sstevel@tonic-gate }
15417c478bd9Sstevel@tonic-gate 
15427c478bd9Sstevel@tonic-gate /*
15437c478bd9Sstevel@tonic-gate  * pcf8584_give_up() returns all resources that were taken over forcefully
15447c478bd9Sstevel@tonic-gate  */
15457c478bd9Sstevel@tonic-gate static void
15467c478bd9Sstevel@tonic-gate pcf8584_give_up(pcf8584_t *i2c, kcondvar_t *waiter, int saved_mode)
15477c478bd9Sstevel@tonic-gate {
15487c478bd9Sstevel@tonic-gate 	i2c->pcf8584_mode = saved_mode;
15497c478bd9Sstevel@tonic-gate 
15507c478bd9Sstevel@tonic-gate 	/*
15517c478bd9Sstevel@tonic-gate 	 * Note that pcf8584_release only wakes up threads waiting to acquire
15527c478bd9Sstevel@tonic-gate 	 * the I2C bus. We still need to wake up the waiter from whom we
15537c478bd9Sstevel@tonic-gate 	 * usurped the bus.
15547c478bd9Sstevel@tonic-gate 	 */
15557c478bd9Sstevel@tonic-gate 	pcf8584_release(i2c, B_TRUE);
15567c478bd9Sstevel@tonic-gate 	if (waiter)
15577c478bd9Sstevel@tonic-gate 		cv_signal(waiter);
15587c478bd9Sstevel@tonic-gate 
15597c478bd9Sstevel@tonic-gate 	mutex_exit(&i2c->pcf8584_imutex);
15607c478bd9Sstevel@tonic-gate }
1561