xref: /titanic_51/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.c (revision 193974072f41a843678abf5f61979c748687e66b)
11c42de6dSgd78059 /*
21c42de6dSgd78059  * CDDL HEADER START
31c42de6dSgd78059  *
41c42de6dSgd78059  * The contents of this file are subject to the terms of the
51c42de6dSgd78059  * Common Development and Distribution License (the "License").
61c42de6dSgd78059  * You may not use this file except in compliance with the License.
71c42de6dSgd78059  *
81c42de6dSgd78059  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91c42de6dSgd78059  * or http://www.opensolaris.org/os/licensing.
101c42de6dSgd78059  * See the License for the specific language governing permissions
111c42de6dSgd78059  * and limitations under the License.
121c42de6dSgd78059  *
131c42de6dSgd78059  * When distributing Covered Code, include this CDDL HEADER in each
141c42de6dSgd78059  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151c42de6dSgd78059  * If applicable, add the following below this CDDL HEADER, with the
161c42de6dSgd78059  * fields enclosed by brackets "[]" replaced with your own identifying
171c42de6dSgd78059  * information: Portions Copyright [yyyy] [name of copyright owner]
181c42de6dSgd78059  *
191c42de6dSgd78059  * CDDL HEADER END
201c42de6dSgd78059  */
211c42de6dSgd78059 /*
22*19397407SSherry Moore  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
231c42de6dSgd78059  * Use is subject to license terms.
241c42de6dSgd78059  */
251c42de6dSgd78059 
261c42de6dSgd78059 
271c42de6dSgd78059 /*
281c42de6dSgd78059  * i2bsc.c is the nexus driver i2c traffic against devices hidden behind the
291c42de6dSgd78059  * Blade Support Chip (BSC).  It supports both interrupt and polled
301c42de6dSgd78059  * mode operation, but defaults to interrupt.
311c42de6dSgd78059  */
321c42de6dSgd78059 
331c42de6dSgd78059 #include <sys/types.h>
341c42de6dSgd78059 #include <sys/conf.h>
351c42de6dSgd78059 #include <sys/file.h>
361c42de6dSgd78059 #include <sys/open.h>
371c42de6dSgd78059 #include <sys/ddi.h>
381c42de6dSgd78059 #include <sys/sunddi.h>
391c42de6dSgd78059 #include <sys/sunndi.h>
401c42de6dSgd78059 #include <sys/modctl.h>
411c42de6dSgd78059 #include <sys/stat.h>
421c42de6dSgd78059 #include <sys/kmem.h>
431c42de6dSgd78059 #include <sys/platform_module.h>
441c42de6dSgd78059 #include <sys/stream.h>
451c42de6dSgd78059 #include <sys/strlog.h>
461c42de6dSgd78059 #include <sys/log.h>
471c42de6dSgd78059 #include <sys/debug.h>
481c42de6dSgd78059 #include <sys/note.h>
491c42de6dSgd78059 
501c42de6dSgd78059 #include <sys/bscbus.h>
511c42de6dSgd78059 #include <sys/lom_ebuscodes.h>
521c42de6dSgd78059 
531c42de6dSgd78059 #include <sys/i2c/clients/i2c_client.h>
541c42de6dSgd78059 #include <sys/i2c/misc/i2c_svc.h>
551c42de6dSgd78059 #include <sys/i2c/misc/i2c_svc_impl.h>
561c42de6dSgd78059 #include <sys/i2c/nexus/i2bsc_impl.h>
571c42de6dSgd78059 
581c42de6dSgd78059 /*
591c42de6dSgd78059  * static function declarations
601c42de6dSgd78059  */
611c42de6dSgd78059 static void i2bsc_resume(dev_info_t *dip);
621c42de6dSgd78059 static void i2bsc_suspend(dev_info_t *dip);
631c42de6dSgd78059 static int i2bsc_bus_ctl(dev_info_t *dip, dev_info_t *rdip,
641c42de6dSgd78059 	ddi_ctl_enum_t op, void *arg, void *result);
651c42de6dSgd78059 static  void i2bsc_acquire(i2bsc_t *, dev_info_t *dip,
661c42de6dSgd78059 	i2c_transfer_t *tp);
671c42de6dSgd78059 static  void i2bsc_release(i2bsc_t *);
681c42de6dSgd78059 static int i2bsc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
691c42de6dSgd78059 static int i2bsc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
701c42de6dSgd78059 static int i2bsc_open(dev_t *devp, int flag, int otyp,
711c42de6dSgd78059     cred_t *cred_p);
721c42de6dSgd78059 static int i2bsc_close(dev_t dev, int flag, int otyp,
731c42de6dSgd78059     cred_t *cred_p);
741c42de6dSgd78059 static int i2bsc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
751c42de6dSgd78059 static int i2bsc_initchild(dev_info_t *dip, dev_info_t *cdip);
761c42de6dSgd78059 static int i2bsc_uninitchild(dev_info_t *dip, dev_info_t *cdip);
771c42de6dSgd78059 static int i2bsc_setup_regs(i2bsc_t *);
781c42de6dSgd78059 static void i2bsc_start_session(i2bsc_t *);
791c42de6dSgd78059 static void i2bsc_fail_session(i2bsc_t *);
801c42de6dSgd78059 static int i2bsc_end_session(i2bsc_t *);
811c42de6dSgd78059 static void i2bsc_free_regs(i2bsc_t *);
821c42de6dSgd78059 static int i2bsc_reportdev(dev_info_t *dip, dev_info_t *rdip);
831c42de6dSgd78059 int i2bsc_transfer(dev_info_t *dip, i2c_transfer_t *tp);
841c42de6dSgd78059 static void i2bsc_trace(i2bsc_t *, char, const char *,
851c42de6dSgd78059     const char *, ...);
861c42de6dSgd78059 static int i2bsc_notify_max_transfer_size(i2bsc_t *);
871c42de6dSgd78059 static int i2bsc_discover_capability(i2bsc_t *);
881c42de6dSgd78059 static void i2bsc_put8(i2bsc_t *, uint8_t, uint8_t, uint8_t);
891c42de6dSgd78059 static uint8_t i2bsc_get8(i2bsc_t *, uint8_t, uint8_t);
901c42de6dSgd78059 static int i2bsc_safe_upload(i2bsc_t *, i2c_transfer_t *);
911c42de6dSgd78059 static boolean_t i2bsc_is_firmware_broken(i2bsc_t *);
921c42de6dSgd78059 
931c42de6dSgd78059 static struct bus_ops i2bsc_busops = {
941c42de6dSgd78059 	BUSO_REV,
951c42de6dSgd78059 	nullbusmap,			/* bus_map */
961c42de6dSgd78059 	NULL,				/* bus_get_intrspec */
971c42de6dSgd78059 	NULL,				/* bus_add_intrspec */
981c42de6dSgd78059 	NULL,				/* bus_remove_intrspec */
991c42de6dSgd78059 	NULL,				/* bus_map_fault */
1001c42de6dSgd78059 	ddi_no_dma_map,			/* bus_dma_map */
1011c42de6dSgd78059 	ddi_no_dma_allochdl,		/* bus_dma_allochdl */
1021c42de6dSgd78059 	ddi_no_dma_freehdl,		/* bus_dma_freehdl */
1031c42de6dSgd78059 	ddi_no_dma_bindhdl,		/* bus_dma_bindhdl */
1041c42de6dSgd78059 	ddi_no_dma_unbindhdl,		/* bus_unbindhdl */
1051c42de6dSgd78059 	ddi_no_dma_flush,		/* bus_dma_flush */
1061c42de6dSgd78059 	ddi_no_dma_win,			/* bus_dma_win */
1071c42de6dSgd78059 	ddi_no_dma_mctl,		/* bus_dma_ctl */
1081c42de6dSgd78059 	i2bsc_bus_ctl,			/* bus_ctl */
1091c42de6dSgd78059 	ddi_bus_prop_op,		/* bus_prop_op */
1101c42de6dSgd78059 	NULL,				/* bus_get_eventcookie */
1111c42de6dSgd78059 	NULL,				/* bus_add_eventcall */
1121c42de6dSgd78059 	NULL,				/* bus_remove_eventcall */
1131c42de6dSgd78059 	NULL,				/* bus_post_event */
1141c42de6dSgd78059 	0,				/* bus_intr_ctl */
1151c42de6dSgd78059 	0,				/* bus_config		*/
1161c42de6dSgd78059 	0,				/* bus_unconfig		*/
1171c42de6dSgd78059 	0,				/* bus_fm_init		*/
1181c42de6dSgd78059 	0,				/* bus_fm_fini		*/
1191c42de6dSgd78059 	0,				/* bus_fm_access_enter	*/
1201c42de6dSgd78059 	0,				/* bus_fm_access_exit	*/
1211c42de6dSgd78059 	0,				/* bus_power		*/
1221c42de6dSgd78059 	i_ddi_intr_ops			/* bus_intr_op		*/
1231c42de6dSgd78059 
1241c42de6dSgd78059 };
1251c42de6dSgd78059 
1261c42de6dSgd78059 struct cb_ops i2bsc_cb_ops = {
1271c42de6dSgd78059 	i2bsc_open,		/* open */
1281c42de6dSgd78059 	i2bsc_close,		/* close */
1291c42de6dSgd78059 	nodev,			/* strategy */
1301c42de6dSgd78059 	nodev,			/* print */
1311c42de6dSgd78059 	nodev,			/* dump */
1321c42de6dSgd78059 	nodev,			/* read */
1331c42de6dSgd78059 	nodev,			/* write */
1341c42de6dSgd78059 	i2bsc_ioctl,		/* ioctl */
1351c42de6dSgd78059 	nodev,			/* devmap */
1361c42de6dSgd78059 	nodev,			/* mmap */
1371c42de6dSgd78059 	nodev,			/* segmap */
1381c42de6dSgd78059 	nochpoll,		/* poll */
1391c42de6dSgd78059 	ddi_prop_op,		/* cb_prop_op */
1401c42de6dSgd78059 	0,			/* streamtab  */
1411c42de6dSgd78059 	D_MP | D_NEW		/* Driver compatibility flag */
1421c42de6dSgd78059 };
1431c42de6dSgd78059 
1441c42de6dSgd78059 static struct dev_ops i2bsc_ops = {
1451c42de6dSgd78059 	DEVO_REV,
1461c42de6dSgd78059 	0,
1471c42de6dSgd78059 	ddi_getinfo_1to1,
1481c42de6dSgd78059 	nulldev,
1491c42de6dSgd78059 	nulldev,
1501c42de6dSgd78059 	i2bsc_attach,
1511c42de6dSgd78059 	i2bsc_detach,
1521c42de6dSgd78059 	nodev,
1531c42de6dSgd78059 	&i2bsc_cb_ops,
154*19397407SSherry Moore 	&i2bsc_busops,
155*19397407SSherry Moore 	NULL,
156*19397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
1571c42de6dSgd78059 };
1581c42de6dSgd78059 
1591c42de6dSgd78059 #ifdef DEBUG
160*19397407SSherry Moore #define	I2BSC_VERSION_STRING "i2bsc driver - Debug"
1611c42de6dSgd78059 #else
162*19397407SSherry Moore #define	I2BSC_VERSION_STRING "i2bsc driver"
1631c42de6dSgd78059 #endif
1641c42de6dSgd78059 
1651c42de6dSgd78059 static struct modldrv modldrv = {
1661c42de6dSgd78059 	&mod_driverops, /* Type of module. This one is a driver */
1671c42de6dSgd78059 	I2BSC_VERSION_STRING,	/* Name of the module. */
1681c42de6dSgd78059 	&i2bsc_ops,		/* driver ops */
1691c42de6dSgd78059 };
1701c42de6dSgd78059 
1711c42de6dSgd78059 static struct modlinkage modlinkage = {
1721c42de6dSgd78059 	MODREV_1,
1731c42de6dSgd78059 	&modldrv,
1741c42de6dSgd78059 	NULL
1751c42de6dSgd78059 };
1761c42de6dSgd78059 
1771c42de6dSgd78059 /*
1781c42de6dSgd78059  * i2bsc soft state
1791c42de6dSgd78059  */
1801c42de6dSgd78059 static void	*i2bsc_state;
1811c42de6dSgd78059 
1821c42de6dSgd78059 i2c_nexus_reg_t i2bsc_regvec = {
1831c42de6dSgd78059 	I2C_NEXUS_REV,
1841c42de6dSgd78059 	i2bsc_transfer,
1851c42de6dSgd78059 };
1861c42de6dSgd78059 
1871c42de6dSgd78059 int
1881c42de6dSgd78059 _init(void)
1891c42de6dSgd78059 {
1901c42de6dSgd78059 	int status;
1911c42de6dSgd78059 
1921c42de6dSgd78059 	status = ddi_soft_state_init(&i2bsc_state, sizeof (i2bsc_t),
1931c42de6dSgd78059 	    I2BSC_INITIAL_SOFT_SPACE);
1941c42de6dSgd78059 	if (status != 0) {
1951c42de6dSgd78059 		return (status);
1961c42de6dSgd78059 	}
1971c42de6dSgd78059 
1981c42de6dSgd78059 	if ((status = mod_install(&modlinkage)) != 0) {
1991c42de6dSgd78059 		ddi_soft_state_fini(&i2bsc_state);
2001c42de6dSgd78059 	}
2011c42de6dSgd78059 
2021c42de6dSgd78059 	return (status);
2031c42de6dSgd78059 }
2041c42de6dSgd78059 
2051c42de6dSgd78059 int
2061c42de6dSgd78059 _fini(void)
2071c42de6dSgd78059 {
2081c42de6dSgd78059 	int status;
2091c42de6dSgd78059 
2101c42de6dSgd78059 	if ((status = mod_remove(&modlinkage)) == 0) {
2111c42de6dSgd78059 		ddi_soft_state_fini(&i2bsc_state);
2121c42de6dSgd78059 	}
2131c42de6dSgd78059 
2141c42de6dSgd78059 	return (status);
2151c42de6dSgd78059 }
2161c42de6dSgd78059 
2171c42de6dSgd78059 /*
2181c42de6dSgd78059  * The loadable-module _info(9E) entry point
2191c42de6dSgd78059  */
2201c42de6dSgd78059 int
2211c42de6dSgd78059 _info(struct modinfo *modinfop)
2221c42de6dSgd78059 {
2231c42de6dSgd78059 	return (mod_info(&modlinkage, modinfop));
2241c42de6dSgd78059 }
2251c42de6dSgd78059 
2261c42de6dSgd78059 static void
2271c42de6dSgd78059 i2bsc_dodetach(dev_info_t *dip)
2281c42de6dSgd78059 {
2291c42de6dSgd78059 	i2bsc_t *i2c;
2301c42de6dSgd78059 	int instance = ddi_get_instance(dip);
2311c42de6dSgd78059 
2321c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
2331c42de6dSgd78059 
2341c42de6dSgd78059 	if ((i2c->i2bsc_attachflags & IMUTEX) != 0) {
2351c42de6dSgd78059 		mutex_destroy(&i2c->i2bsc_imutex);
2361c42de6dSgd78059 		cv_destroy(&i2c->i2bsc_icv);
2371c42de6dSgd78059 	}
2381c42de6dSgd78059 	if ((i2c->i2bsc_attachflags & SETUP_REGS) != 0) {
2391c42de6dSgd78059 		i2bsc_free_regs(i2c);
2401c42de6dSgd78059 	}
2411c42de6dSgd78059 	if ((i2c->i2bsc_attachflags & NEXUS_REGISTER) != 0) {
2421c42de6dSgd78059 		i2c_nexus_unregister(dip);
2431c42de6dSgd78059 	}
2441c42de6dSgd78059 	if ((i2c->i2bsc_attachflags & MINOR_NODE) != 0) {
2451c42de6dSgd78059 		ddi_remove_minor_node(dip, NULL);
2461c42de6dSgd78059 	}
2471c42de6dSgd78059 
2481c42de6dSgd78059 	ddi_soft_state_free(i2bsc_state, instance);
2491c42de6dSgd78059 }
2501c42de6dSgd78059 
2511c42de6dSgd78059 static int
2521c42de6dSgd78059 i2bsc_doattach(dev_info_t *dip)
2531c42de6dSgd78059 {
2541c42de6dSgd78059 	i2bsc_t *i2c;
2551c42de6dSgd78059 	int instance = ddi_get_instance(dip);
2561c42de6dSgd78059 
2571c42de6dSgd78059 	/*
2581c42de6dSgd78059 	 * Allocate soft state structure.
2591c42de6dSgd78059 	 */
2601c42de6dSgd78059 	if (ddi_soft_state_zalloc(i2bsc_state, instance) != DDI_SUCCESS) {
2611c42de6dSgd78059 		return (DDI_FAILURE);
2621c42de6dSgd78059 	}
2631c42de6dSgd78059 
2641c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
2651c42de6dSgd78059 
2661c42de6dSgd78059 	i2c->majornum = ddi_driver_major(dip);
2671c42de6dSgd78059 	i2c->minornum = instance;
2681c42de6dSgd78059 	i2c->i2bsc_dip = dip;
2691c42de6dSgd78059 	i2c->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2701c42de6dSgd78059 	    DDI_PROP_DONTPASS, "debug", 0);
2711c42de6dSgd78059 
2721c42de6dSgd78059 	(void) snprintf(i2c->i2bsc_name, sizeof (i2c->i2bsc_name),
2731c42de6dSgd78059 	    "%s_%d", ddi_node_name(dip), instance);
2741c42de6dSgd78059 
2751c42de6dSgd78059 	if (i2bsc_setup_regs(i2c) != DDI_SUCCESS) {
2761c42de6dSgd78059 		goto bad;
2771c42de6dSgd78059 	}
2781c42de6dSgd78059 
2791c42de6dSgd78059 	i2c->i2bsc_attachflags |= SETUP_REGS;
2801c42de6dSgd78059 
2811c42de6dSgd78059 	mutex_init(&i2c->i2bsc_imutex, NULL, MUTEX_DRIVER,
2821c42de6dSgd78059 	    (void *) 0);
2831c42de6dSgd78059 	cv_init(&i2c->i2bsc_icv, NULL, CV_DRIVER, NULL);
2841c42de6dSgd78059 	i2c->i2bsc_attachflags |= IMUTEX;
2851c42de6dSgd78059 
2861c42de6dSgd78059 	i2c_nexus_register(dip, &i2bsc_regvec);
2871c42de6dSgd78059 	i2c->i2bsc_attachflags |= NEXUS_REGISTER;
2881c42de6dSgd78059 
2891c42de6dSgd78059 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
2901c42de6dSgd78059 	    DDI_NT_NEXUS, 0) == DDI_FAILURE) {
2911c42de6dSgd78059 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed",
2921c42de6dSgd78059 		    i2c->i2bsc_name);
2931c42de6dSgd78059 		goto bad;
2941c42de6dSgd78059 	}
2951c42de6dSgd78059 
2961c42de6dSgd78059 	i2c->i2bsc_attachflags |= MINOR_NODE;
2971c42de6dSgd78059 
2981c42de6dSgd78059 	/*
2991c42de6dSgd78059 	 * Now actually start talking to the microcontroller.  The first
3001c42de6dSgd78059 	 * thing to check is whether the firmware is broken.
3011c42de6dSgd78059 	 */
3021c42de6dSgd78059 	if (i2bsc_is_firmware_broken(i2c)) {
3031c42de6dSgd78059 		cmn_err(CE_WARN, "Underlying BSC hardware not communicating;"
3041c42de6dSgd78059 		    " shutting down my i2c services");
3051c42de6dSgd78059 		goto bad;
3061c42de6dSgd78059 	}
3071c42de6dSgd78059 
3081c42de6dSgd78059 	i2c->i2bsc_attachflags |= FIRMWARE_ALIVE;
3091c42de6dSgd78059 
3101c42de6dSgd78059 	/*
3111c42de6dSgd78059 	 * Now see if the BSC chip supports the i2c service we rely upon.
3121c42de6dSgd78059 	 */
3131c42de6dSgd78059 	(void) i2bsc_discover_capability(i2c);
3141c42de6dSgd78059 
3151c42de6dSgd78059 	if (i2bsc_notify_max_transfer_size(i2c) == DDI_SUCCESS)
3161c42de6dSgd78059 		i2c->i2bsc_attachflags |= TRANSFER_SZ;
3171c42de6dSgd78059 
3181c42de6dSgd78059 	i2bsc_trace(i2c, 'A', "i2bsc_doattach", "attachflags %d",
3191c42de6dSgd78059 	    i2c->i2bsc_attachflags);
3201c42de6dSgd78059 
3211c42de6dSgd78059 	return (DDI_SUCCESS);
3221c42de6dSgd78059 
3231c42de6dSgd78059 bad:
3241c42de6dSgd78059 	i2bsc_dodetach(dip);
3251c42de6dSgd78059 
3261c42de6dSgd78059 	return (DDI_FAILURE);
3271c42de6dSgd78059 }
3281c42de6dSgd78059 
3291c42de6dSgd78059 static int
3301c42de6dSgd78059 i2bsc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3311c42de6dSgd78059 {
3321c42de6dSgd78059 	switch (cmd) {
3331c42de6dSgd78059 		case DDI_ATTACH:
3341c42de6dSgd78059 		return (i2bsc_doattach(dip));
3351c42de6dSgd78059 
3361c42de6dSgd78059 		case DDI_RESUME:
3371c42de6dSgd78059 		i2bsc_resume(dip);
3381c42de6dSgd78059 		return (DDI_SUCCESS);
3391c42de6dSgd78059 
3401c42de6dSgd78059 		default:
3411c42de6dSgd78059 		return (DDI_FAILURE);
3421c42de6dSgd78059 	}
3431c42de6dSgd78059 }
3441c42de6dSgd78059 
3451c42de6dSgd78059 static int
3461c42de6dSgd78059 i2bsc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3471c42de6dSgd78059 {
3481c42de6dSgd78059 	switch (cmd) {
3491c42de6dSgd78059 	case DDI_DETACH:
3501c42de6dSgd78059 		i2bsc_dodetach(dip);
3511c42de6dSgd78059 		return (DDI_SUCCESS);
3521c42de6dSgd78059 
3531c42de6dSgd78059 	case DDI_SUSPEND:
3541c42de6dSgd78059 		i2bsc_suspend(dip);
3551c42de6dSgd78059 		return (DDI_SUCCESS);
3561c42de6dSgd78059 
3571c42de6dSgd78059 	default:
3581c42de6dSgd78059 		return (DDI_FAILURE);
3591c42de6dSgd78059 	}
3601c42de6dSgd78059 }
3611c42de6dSgd78059 
3621c42de6dSgd78059 /*ARGSUSED*/
3631c42de6dSgd78059 static int
3641c42de6dSgd78059 i2bsc_open(dev_t  *devp,  int  flag,  int  otyp,  cred_t *cred_p)
3651c42de6dSgd78059 {
3661c42de6dSgd78059 	int instance;
3671c42de6dSgd78059 	i2bsc_t *i2c;
3681c42de6dSgd78059 
3691c42de6dSgd78059 	/*
3701c42de6dSgd78059 	 * Make sure the open is for the right file type
3711c42de6dSgd78059 	 */
3721c42de6dSgd78059 	if (otyp != OTYP_CHR)
3731c42de6dSgd78059 		return (EINVAL);
3741c42de6dSgd78059 
3751c42de6dSgd78059 	instance = getminor(*devp);
3761c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
3771c42de6dSgd78059 	if (i2c == NULL)
3781c42de6dSgd78059 		return (ENXIO);
3791c42de6dSgd78059 
3801c42de6dSgd78059 	/*
3811c42de6dSgd78059 	 * Enforce exclusive access
3821c42de6dSgd78059 	 */
3831c42de6dSgd78059 	mutex_enter(&i2c->i2bsc_imutex);
3841c42de6dSgd78059 	if (i2c->i2bsc_open) {
3851c42de6dSgd78059 		mutex_exit(&i2c->i2bsc_imutex);
3861c42de6dSgd78059 		return (EBUSY);
3871c42de6dSgd78059 	} else
3881c42de6dSgd78059 		i2c->i2bsc_open = 1;
3891c42de6dSgd78059 
3901c42de6dSgd78059 	mutex_exit(&i2c->i2bsc_imutex);
3911c42de6dSgd78059 
3921c42de6dSgd78059 	return (0);
3931c42de6dSgd78059 }
3941c42de6dSgd78059 
3951c42de6dSgd78059 /*ARGSUSED*/
3961c42de6dSgd78059 static int
3971c42de6dSgd78059 i2bsc_close(dev_t  dev,  int  flag,  int  otyp,  cred_t *cred_p)
3981c42de6dSgd78059 {
3991c42de6dSgd78059 	int instance;
4001c42de6dSgd78059 	i2bsc_t *i2c;
4011c42de6dSgd78059 
4021c42de6dSgd78059 	/*
4031c42de6dSgd78059 	 * Make sure the close is for the right file type
4041c42de6dSgd78059 	 */
4051c42de6dSgd78059 	if (otyp != OTYP_CHR)
4061c42de6dSgd78059 		return (EINVAL);
4071c42de6dSgd78059 
4081c42de6dSgd78059 	instance = getminor(dev);
4091c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
4101c42de6dSgd78059 	if (i2c == NULL)
4111c42de6dSgd78059 		return (ENXIO);
4121c42de6dSgd78059 
4131c42de6dSgd78059 	mutex_enter(&i2c->i2bsc_imutex);
4141c42de6dSgd78059 	i2c->i2bsc_open = 0;
4151c42de6dSgd78059 	mutex_exit(&i2c->i2bsc_imutex);
4161c42de6dSgd78059 
4171c42de6dSgd78059 	return (0);
4181c42de6dSgd78059 }
4191c42de6dSgd78059 
4201c42de6dSgd78059 /*ARGSUSED*/
4211c42de6dSgd78059 static int
4221c42de6dSgd78059 i2bsc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
4231c42de6dSgd78059 	int *rvalp)
4241c42de6dSgd78059 {
4251c42de6dSgd78059 	i2bsc_t *i2c;
4261c42de6dSgd78059 	dev_info_t *self;
4271c42de6dSgd78059 	dev_info_t *child;
4281c42de6dSgd78059 	struct devctl_iocdata *dcp;
4291c42de6dSgd78059 	int rv;
4301c42de6dSgd78059 
4311c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, getminor(dev));
4321c42de6dSgd78059 
4331c42de6dSgd78059 	if (i2c == NULL)
4341c42de6dSgd78059 		return (ENXIO);
4351c42de6dSgd78059 
4361c42de6dSgd78059 	self = (dev_info_t *)i2c->i2bsc_dip;
4371c42de6dSgd78059 
4381c42de6dSgd78059 	/*
4391c42de6dSgd78059 	 * read devctl ioctl data
4401c42de6dSgd78059 	 */
4411c42de6dSgd78059 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) {
4421c42de6dSgd78059 		return (EFAULT);
4431c42de6dSgd78059 	}
4441c42de6dSgd78059 
4451c42de6dSgd78059 	switch (cmd) {
4461c42de6dSgd78059 		case DEVCTL_BUS_DEV_CREATE:
4471c42de6dSgd78059 		rv = ndi_dc_devi_create(dcp, self, 0, NULL);
4481c42de6dSgd78059 		break;
4491c42de6dSgd78059 
4501c42de6dSgd78059 		case DEVCTL_DEVICE_REMOVE:
4511c42de6dSgd78059 		if (ndi_dc_getname(dcp) == NULL ||
4521c42de6dSgd78059 		    ndi_dc_getaddr(dcp) == NULL) {
4531c42de6dSgd78059 			rv = EINVAL;
4541c42de6dSgd78059 			break;
4551c42de6dSgd78059 		}
4561c42de6dSgd78059 
4571c42de6dSgd78059 		/*
4581c42de6dSgd78059 		 * lookup and hold child device
4591c42de6dSgd78059 		 */
4601c42de6dSgd78059 		child = ndi_devi_find(self,
4611c42de6dSgd78059 		    ndi_dc_getname(dcp), ndi_dc_getaddr(dcp));
4621c42de6dSgd78059 		if (child == NULL) {
4631c42de6dSgd78059 			rv = ENXIO;
4641c42de6dSgd78059 			break;
4651c42de6dSgd78059 		}
4661c42de6dSgd78059 
4671c42de6dSgd78059 		if ((rv = ndi_devi_offline(child, NDI_DEVI_REMOVE)) !=
4681c42de6dSgd78059 		    NDI_SUCCESS) {
4691c42de6dSgd78059 			rv = (rv == NDI_BUSY) ? EBUSY : EIO;
4701c42de6dSgd78059 		}
4711c42de6dSgd78059 
4721c42de6dSgd78059 		break;
4731c42de6dSgd78059 
4741c42de6dSgd78059 		default:
4751c42de6dSgd78059 		rv = ENOTSUP;
4761c42de6dSgd78059 	}
4771c42de6dSgd78059 
4781c42de6dSgd78059 	ndi_dc_freehdl(dcp);
4791c42de6dSgd78059 
4801c42de6dSgd78059 	return (rv);
4811c42de6dSgd78059 }
4821c42de6dSgd78059 
4831c42de6dSgd78059 static int
4841c42de6dSgd78059 i2bsc_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
4851c42de6dSgd78059     void *arg, void *result)
4861c42de6dSgd78059 {
4871c42de6dSgd78059 	i2bsc_t	*i2c;
4881c42de6dSgd78059 	int instance = ddi_get_instance(dip);
4891c42de6dSgd78059 
4901c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
4911c42de6dSgd78059 
4921c42de6dSgd78059 	i2bsc_trace(i2c, 'A', "i2bsc_bus_ctl", "dip/rdip,op/arg"
4931c42de6dSgd78059 	    " %p/%p,%d/%p", dip, rdip, (int)op, arg);
4941c42de6dSgd78059 
4951c42de6dSgd78059 	switch (op) {
4961c42de6dSgd78059 		case DDI_CTLOPS_INITCHILD:
4971c42de6dSgd78059 		return (i2bsc_initchild(dip, (dev_info_t *)arg));
4981c42de6dSgd78059 
4991c42de6dSgd78059 		case DDI_CTLOPS_UNINITCHILD:
5001c42de6dSgd78059 		return (i2bsc_uninitchild(dip, (dev_info_t *)arg));
5011c42de6dSgd78059 
5021c42de6dSgd78059 		case DDI_CTLOPS_REPORTDEV:
5031c42de6dSgd78059 		return (i2bsc_reportdev(dip, rdip));
5041c42de6dSgd78059 
5051c42de6dSgd78059 		case DDI_CTLOPS_DMAPMAPC:
5061c42de6dSgd78059 		case DDI_CTLOPS_POKE:
5071c42de6dSgd78059 		case DDI_CTLOPS_PEEK:
5081c42de6dSgd78059 		case DDI_CTLOPS_IOMIN:
5091c42de6dSgd78059 		case DDI_CTLOPS_REPORTINT:
5101c42de6dSgd78059 		case DDI_CTLOPS_SIDDEV:
5111c42de6dSgd78059 		case DDI_CTLOPS_SLAVEONLY:
5121c42de6dSgd78059 		case DDI_CTLOPS_AFFINITY:
5131c42de6dSgd78059 		case DDI_CTLOPS_PTOB:
5141c42de6dSgd78059 		case DDI_CTLOPS_BTOP:
5151c42de6dSgd78059 		case DDI_CTLOPS_BTOPR:
5161c42de6dSgd78059 		case DDI_CTLOPS_DVMAPAGESIZE:
5171c42de6dSgd78059 		return (DDI_FAILURE);
5181c42de6dSgd78059 
5191c42de6dSgd78059 		default:
5201c42de6dSgd78059 		return (ddi_ctlops(dip, rdip, op, arg, result));
5211c42de6dSgd78059 	}
5221c42de6dSgd78059 }
5231c42de6dSgd78059 
5241c42de6dSgd78059 /*
5251c42de6dSgd78059  * i2bsc_suspend() is called before the system suspends.  Existing
5261c42de6dSgd78059  * transfer in progress or waiting will complete, but new transfers are
5271c42de6dSgd78059  * effectively blocked by "acquiring" the bus.
5281c42de6dSgd78059  */
5291c42de6dSgd78059 static void
5301c42de6dSgd78059 i2bsc_suspend(dev_info_t *dip)
5311c42de6dSgd78059 {
5321c42de6dSgd78059 	i2bsc_t *i2c;
5331c42de6dSgd78059 	int instance;
5341c42de6dSgd78059 
5351c42de6dSgd78059 	instance = ddi_get_instance(dip);
5361c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
5371c42de6dSgd78059 
5381c42de6dSgd78059 	i2bsc_acquire(i2c, NULL, NULL);
5391c42de6dSgd78059 }
5401c42de6dSgd78059 
5411c42de6dSgd78059 /*
5421c42de6dSgd78059  * i2bsc_resume() is called when the system resumes from CPR.  It releases
5431c42de6dSgd78059  * the hold that was placed on the i2c bus, which allows any real
5441c42de6dSgd78059  * transfers to continue.
5451c42de6dSgd78059  */
5461c42de6dSgd78059 static void
5471c42de6dSgd78059 i2bsc_resume(dev_info_t *dip)
5481c42de6dSgd78059 {
5491c42de6dSgd78059 	i2bsc_t *i2c;
5501c42de6dSgd78059 	int instance;
5511c42de6dSgd78059 
5521c42de6dSgd78059 	instance = ddi_get_instance(dip);
5531c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
5541c42de6dSgd78059 
5551c42de6dSgd78059 	i2bsc_release(i2c);
5561c42de6dSgd78059 }
5571c42de6dSgd78059 
5581c42de6dSgd78059 /*
5591c42de6dSgd78059  * i2bsc_acquire() is called by a thread wishing to "own" the I2C bus.
5601c42de6dSgd78059  * It should not be held across multiple transfers.
5611c42de6dSgd78059  */
5621c42de6dSgd78059 static void
5631c42de6dSgd78059 i2bsc_acquire(i2bsc_t *i2c, dev_info_t *dip, i2c_transfer_t *tp)
5641c42de6dSgd78059 {
5651c42de6dSgd78059 	mutex_enter(&i2c->i2bsc_imutex);
5661c42de6dSgd78059 	while (i2c->i2bsc_busy) {
5671c42de6dSgd78059 		cv_wait(&i2c->i2bsc_icv, &i2c->i2bsc_imutex);
5681c42de6dSgd78059 	}
5691c42de6dSgd78059 	i2c->i2bsc_busy = 1;
5701c42de6dSgd78059 	i2c->i2bsc_cur_tran = tp;
5711c42de6dSgd78059 	i2c->i2bsc_cur_dip = dip;
5721c42de6dSgd78059 	mutex_exit(&i2c->i2bsc_imutex);
5731c42de6dSgd78059 }
5741c42de6dSgd78059 
5751c42de6dSgd78059 /*
5761c42de6dSgd78059  * i2bsc_release() is called to release a hold made by i2bsc_acquire().
5771c42de6dSgd78059  */
5781c42de6dSgd78059 static void
5791c42de6dSgd78059 i2bsc_release(i2bsc_t *i2c)
5801c42de6dSgd78059 {
5811c42de6dSgd78059 	mutex_enter(&i2c->i2bsc_imutex);
5821c42de6dSgd78059 	i2c->i2bsc_busy = 0;
5831c42de6dSgd78059 	i2c->i2bsc_cur_tran = NULL;
5841c42de6dSgd78059 	cv_signal(&i2c->i2bsc_icv);
5851c42de6dSgd78059 	mutex_exit(&i2c->i2bsc_imutex);
5861c42de6dSgd78059 }
5871c42de6dSgd78059 
5881c42de6dSgd78059 static int
5891c42de6dSgd78059 i2bsc_initchild(dev_info_t *dip, dev_info_t *cdip)
5901c42de6dSgd78059 {
5911c42de6dSgd78059 	i2bsc_t *i2c;
5921c42de6dSgd78059 	int32_t address_cells;
5931c42de6dSgd78059 	int len;
5941c42de6dSgd78059 	int32_t regs[3];
5951c42de6dSgd78059 	int err;
5961c42de6dSgd78059 	i2bsc_ppvt_t *ppvt;
5971c42de6dSgd78059 	char name[30];
5981c42de6dSgd78059 
5991c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, ddi_get_instance(dip));
6001c42de6dSgd78059 
6011c42de6dSgd78059 	i2bsc_trace(i2c, 'A', "i2bsc_initchild", "dip/cdip %p/%p", dip, cdip);
6021c42de6dSgd78059 
6031c42de6dSgd78059 	ppvt = kmem_alloc(sizeof (i2bsc_ppvt_t), KM_SLEEP);
6041c42de6dSgd78059 
6051c42de6dSgd78059 	len = sizeof (address_cells);
6061c42de6dSgd78059 
6071c42de6dSgd78059 	err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
6081c42de6dSgd78059 	    DDI_PROP_CANSLEEP, "#address-cells",
6091c42de6dSgd78059 	    (caddr_t)&address_cells, &len);
6101c42de6dSgd78059 	if (err != DDI_PROP_SUCCESS || len != sizeof (address_cells)) {
6111c42de6dSgd78059 		return (DDI_FAILURE);
6121c42de6dSgd78059 	}
6131c42de6dSgd78059 
6141c42de6dSgd78059 	len = sizeof (regs);
6151c42de6dSgd78059 	err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
6161c42de6dSgd78059 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
6171c42de6dSgd78059 	    "reg", (caddr_t)regs, &len);
6181c42de6dSgd78059 	if (err != DDI_PROP_SUCCESS)
6191c42de6dSgd78059 		return (DDI_FAILURE);
6201c42de6dSgd78059 
6211c42de6dSgd78059 	if (address_cells == 1) {
6221c42de6dSgd78059 		ppvt->i2bsc_ppvt_bus = I2BSC_DEFAULT_BUS;
6231c42de6dSgd78059 		ppvt->i2bsc_ppvt_addr = regs[0];
6241c42de6dSgd78059 		(void) sprintf(name, "%x", regs[0]);
6251c42de6dSgd78059 		i2bsc_trace(i2c, 'A', "i2bsc_initchild", "#address-cells = 1"
6261c42de6dSgd78059 		    " regs[0] = %d", regs[0]);
6271c42de6dSgd78059 	} else if (address_cells == 2) {
6281c42de6dSgd78059 		ppvt->i2bsc_ppvt_bus = regs[0];
6291c42de6dSgd78059 		ppvt->i2bsc_ppvt_addr = regs[1];
6301c42de6dSgd78059 		(void) sprintf(name, "%x,%x", regs[0], regs[1]);
6311c42de6dSgd78059 		i2bsc_trace(i2c, 'A', "i2bsc_initchild", "#address-cells = 2"
6321c42de6dSgd78059 		    " regs[0] = %d, regs[1] = %d", regs[0], regs[1]);
6331c42de6dSgd78059 	} else {
6341c42de6dSgd78059 		return (DDI_FAILURE);
6351c42de6dSgd78059 	}
6361c42de6dSgd78059 
6371c42de6dSgd78059 	/*
6381c42de6dSgd78059 	 * Attach the parent's private data structure to the child's devinfo
6391c42de6dSgd78059 	 * node, and store the child's address on the nexus in the child's
6401c42de6dSgd78059 	 * devinfo node.
6411c42de6dSgd78059 	 */
6421c42de6dSgd78059 	ddi_set_parent_data(cdip, ppvt);
6431c42de6dSgd78059 	ddi_set_name_addr(cdip, name);
6441c42de6dSgd78059 
6451c42de6dSgd78059 	i2bsc_trace(i2c, 'A', "i2bsc_initchild", "success(%s)",
6461c42de6dSgd78059 	    ddi_node_name(cdip));
6471c42de6dSgd78059 
6481c42de6dSgd78059 	return (DDI_SUCCESS);
6491c42de6dSgd78059 }
6501c42de6dSgd78059 
6511c42de6dSgd78059 static int
6521c42de6dSgd78059 i2bsc_uninitchild(dev_info_t *dip, dev_info_t *cdip)
6531c42de6dSgd78059 {
6541c42de6dSgd78059 	i2bsc_t *i2c;
6551c42de6dSgd78059 	i2bsc_ppvt_t *ppvt;
6561c42de6dSgd78059 
6571c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, ddi_get_instance(dip));
6581c42de6dSgd78059 
6591c42de6dSgd78059 	i2bsc_trace(i2c, 'D', "i2bsc_uninitchild", "dip/cdip %p/%p", dip, cdip);
6601c42de6dSgd78059 
6611c42de6dSgd78059 	ppvt = ddi_get_parent_data(cdip);
6621c42de6dSgd78059 	kmem_free(ppvt, sizeof (i2bsc_ppvt_t));
6631c42de6dSgd78059 
6641c42de6dSgd78059 	ddi_set_parent_data(cdip, NULL);
6651c42de6dSgd78059 	ddi_set_name_addr(cdip, NULL);
6661c42de6dSgd78059 
6671c42de6dSgd78059 	i2bsc_trace(i2c, 'D', "i2bsc_uninitchild", "success(%s)",
6681c42de6dSgd78059 	    ddi_node_name(cdip));
6691c42de6dSgd78059 
6701c42de6dSgd78059 	return (DDI_SUCCESS);
6711c42de6dSgd78059 }
6721c42de6dSgd78059 
6731c42de6dSgd78059 /*
6741c42de6dSgd78059  * i2bsc_setup_regs() is called to map in registers specific to
6751c42de6dSgd78059  * the i2bsc.
6761c42de6dSgd78059  */
6771c42de6dSgd78059 static int
6781c42de6dSgd78059 i2bsc_setup_regs(i2bsc_t *i2c)
6791c42de6dSgd78059 {
6801c42de6dSgd78059 	int nregs;
6811c42de6dSgd78059 
6821c42de6dSgd78059 	i2c->bscbus_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
6831c42de6dSgd78059 	i2c->bscbus_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
6841c42de6dSgd78059 	i2c->bscbus_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
6851c42de6dSgd78059 
6861c42de6dSgd78059 	if (ddi_dev_nregs(i2c->i2bsc_dip, &nregs) != DDI_SUCCESS) {
6871c42de6dSgd78059 		return (DDI_FAILURE);
6881c42de6dSgd78059 	}
6891c42de6dSgd78059 
6901c42de6dSgd78059 	if (nregs < 1) {
6911c42de6dSgd78059 		return (DDI_FAILURE);
6921c42de6dSgd78059 	}
6931c42de6dSgd78059 
6941c42de6dSgd78059 	if (ddi_regs_map_setup(i2c->i2bsc_dip, 0,
6951c42de6dSgd78059 	    (caddr_t *)&i2c->bscbus_regs, 0, 0, &i2c->bscbus_attr,
6961c42de6dSgd78059 	    &i2c->bscbus_handle) != DDI_SUCCESS) {
6971c42de6dSgd78059 		return (DDI_FAILURE);
6981c42de6dSgd78059 	}
6991c42de6dSgd78059 
7001c42de6dSgd78059 	return (DDI_SUCCESS);
7011c42de6dSgd78059 }
7021c42de6dSgd78059 
7031c42de6dSgd78059 /*
7041c42de6dSgd78059  * i2bsc_free_regs() frees any registers previously
7051c42de6dSgd78059  * allocated.
7061c42de6dSgd78059  */
7071c42de6dSgd78059 static void
7081c42de6dSgd78059 i2bsc_free_regs(i2bsc_t *i2c)
7091c42de6dSgd78059 {
7101c42de6dSgd78059 	if (i2c->bscbus_regs != NULL) {
7111c42de6dSgd78059 		ddi_regs_map_free(&i2c->bscbus_handle);
7121c42de6dSgd78059 	}
7131c42de6dSgd78059 }
7141c42de6dSgd78059 
7151c42de6dSgd78059 /*ARGSUSED*/
7161c42de6dSgd78059 static int
7171c42de6dSgd78059 i2bsc_reportdev(dev_info_t *dip, dev_info_t *rdip)
7181c42de6dSgd78059 {
7191c42de6dSgd78059 	if (rdip == (dev_info_t *)0)
7201c42de6dSgd78059 		return (DDI_FAILURE);
7211c42de6dSgd78059 
7221c42de6dSgd78059 	cmn_err(CE_CONT, "?i2bsc-device: %s@%s, %s%d\n",
7231c42de6dSgd78059 	    ddi_node_name(rdip), ddi_get_name_addr(rdip), ddi_driver_name(rdip),
7241c42de6dSgd78059 	    ddi_get_instance(rdip));
7251c42de6dSgd78059 
7261c42de6dSgd78059 	return (DDI_SUCCESS);
7271c42de6dSgd78059 }
7281c42de6dSgd78059 
7291c42de6dSgd78059 /*
7301c42de6dSgd78059  * I/O Functions
7311c42de6dSgd78059  *
7321c42de6dSgd78059  * i2bsc_{put,get}8_once are wrapper functions to ddi_{get,put}8.
7331c42de6dSgd78059  * i2bsc_{put,get}8 are equivalent functions but with retry code.
7341c42de6dSgd78059  * i2bsc_bscbus_state determines underlying bus error status.
7351c42de6dSgd78059  * i2bsc_clear_acc_fault clears the underlying bus error status.
7361c42de6dSgd78059  *
7371c42de6dSgd78059  * I/O Flags
7381c42de6dSgd78059  *
7391c42de6dSgd78059  * bscbus_fault	   -	Error register in underlying bus for last IO operation.
7401c42de6dSgd78059  * session_failure - 	Set by any failed IO command.  This is a sticky flag
7411c42de6dSgd78059  * 			reset explicitly using i2bsc_start_session
7421c42de6dSgd78059  *
7431c42de6dSgd78059  * Session Management
7441c42de6dSgd78059  *
7451c42de6dSgd78059  * i2bsc_{start,end}_session need to be used to detect an error across multiple
7461c42de6dSgd78059  * gets/puts rather than having to test for an error on each get/put.
7471c42de6dSgd78059  */
7481c42de6dSgd78059 
7491c42de6dSgd78059 static int i2bsc_bscbus_state(i2bsc_t *i2c)
7501c42de6dSgd78059 {
7511c42de6dSgd78059 	uint32_t retval;
7521c42de6dSgd78059 
7531c42de6dSgd78059 	retval = ddi_get32(i2c->bscbus_handle,
7541c42de6dSgd78059 	    (uint32_t *)I2BSC_NEXUS_ADDR(i2c, EBUS_CMD_SPACE_GENERIC,
7551c42de6dSgd78059 	    LOMBUS_FAULT_REG));
7561c42de6dSgd78059 	i2c->bscbus_fault = retval;
7571c42de6dSgd78059 
7581c42de6dSgd78059 	return ((retval == 0) ? DDI_SUCCESS : DDI_FAILURE);
7591c42de6dSgd78059 }
7601c42de6dSgd78059 
7611c42de6dSgd78059 static void i2bsc_clear_acc_fault(i2bsc_t *i2c)
7621c42de6dSgd78059 {
7631c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_clear_acc_fault", "clearing acc fault");
7641c42de6dSgd78059 	ddi_put32(i2c->bscbus_handle,
7651c42de6dSgd78059 	    (uint32_t *)I2BSC_NEXUS_ADDR(i2c, EBUS_CMD_SPACE_GENERIC,
7661c42de6dSgd78059 	    LOMBUS_FAULT_REG), 0);
7671c42de6dSgd78059 }
7681c42de6dSgd78059 
7691c42de6dSgd78059 static void
7701c42de6dSgd78059 i2bsc_start_session(i2bsc_t *i2c)
7711c42de6dSgd78059 {
7721c42de6dSgd78059 	i2bsc_trace(i2c, 'S', "i2bsc_start_session", "session started");
7731c42de6dSgd78059 	i2c->bscbus_session_failure = 0;
7741c42de6dSgd78059 }
7751c42de6dSgd78059 
7761c42de6dSgd78059 static void
7771c42de6dSgd78059 i2bsc_fail_session(i2bsc_t *i2c)
7781c42de6dSgd78059 {
7791c42de6dSgd78059 	i2bsc_trace(i2c, 'S', "i2bsc_fail_session", "session failed");
7801c42de6dSgd78059 	i2c->bscbus_session_failure = 1;
7811c42de6dSgd78059 }
7821c42de6dSgd78059 
7831c42de6dSgd78059 static int
7841c42de6dSgd78059 i2bsc_end_session(i2bsc_t *i2c)
7851c42de6dSgd78059 {
7861c42de6dSgd78059 	/*
7871c42de6dSgd78059 	 * The ONLY way to get the session status is to end the session.
7881c42de6dSgd78059 	 * If clients of the session interface ever wanted the status mid-way
7891c42de6dSgd78059 	 * then they are really working with multiple contigious sessions.
7901c42de6dSgd78059 	 */
7911c42de6dSgd78059 	i2bsc_trace(i2c, 'S', "i2bsc_end_session", "session ended with %d",
7921c42de6dSgd78059 	    i2c->bscbus_session_failure);
7931c42de6dSgd78059 	return ((i2c->bscbus_session_failure) ? DDI_FAILURE : DDI_SUCCESS);
7941c42de6dSgd78059 }
7951c42de6dSgd78059 
7961c42de6dSgd78059 static boolean_t
7971c42de6dSgd78059 i2bsc_is_firmware_broken(i2bsc_t *i2c)
7981c42de6dSgd78059 {
7991c42de6dSgd78059 	int i;
8001c42de6dSgd78059 	int niterations = I2BSC_SHORT_RETRY_LIMIT;
8011c42de6dSgd78059 
8021c42de6dSgd78059 	i2bsc_trace(i2c, 'A', "i2bsc_is_firmware_broken", "called");
8031c42de6dSgd78059 
8041c42de6dSgd78059 	for (i = 0; i < niterations; i++) {
8051c42de6dSgd78059 		(void) ddi_get8(i2c->bscbus_handle,
8061c42de6dSgd78059 		    I2BSC_NEXUS_ADDR(i2c, EBUS_CMD_SPACE_I2C,
8071c42de6dSgd78059 		    EBUS_IDX12_RESULT));
8081c42de6dSgd78059 		if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS) {
8091c42de6dSgd78059 			i2bsc_clear_acc_fault(i2c);
8101c42de6dSgd78059 			continue;
8111c42de6dSgd78059 		} else {
8121c42de6dSgd78059 			/*
8131c42de6dSgd78059 			 * Firmware communication succeeded.
8141c42de6dSgd78059 			 */
8151c42de6dSgd78059 			i2bsc_trace(i2c, 'A', "i2bsc_is_firmware_broken",
8161c42de6dSgd78059 			    "firmware communications okay");
8171c42de6dSgd78059 			return (B_FALSE);
8181c42de6dSgd78059 		}
8191c42de6dSgd78059 	}
8201c42de6dSgd78059 
8211c42de6dSgd78059 	/*
8221c42de6dSgd78059 	 * Firmware is not communicative.  Some possible causes :
8231c42de6dSgd78059 	 *	Broken hardware
8241c42de6dSgd78059 	 *	BSC held in reset
8251c42de6dSgd78059 	 *	Corrupt BSC image
8261c42de6dSgd78059 	 *	OBP incompatiblity preventing drivers loading properly
8271c42de6dSgd78059 	 */
8281c42de6dSgd78059 	i2bsc_trace(i2c, 'A', "i2bsc_is_firmware_broken", "%d read fails",
8291c42de6dSgd78059 	    niterations);
8301c42de6dSgd78059 	return (B_TRUE);
8311c42de6dSgd78059 }
8321c42de6dSgd78059 
8331c42de6dSgd78059 static void
8341c42de6dSgd78059 i2bsc_put8(i2bsc_t *i2c, uint8_t space, uint8_t index, uint8_t value)
8351c42de6dSgd78059 {
8361c42de6dSgd78059 	int retryable = I2BSC_RETRY_LIMIT;
8371c42de6dSgd78059 
8381c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_put8", "(space,index)<-val (%d,%d)<-%d",
8391c42de6dSgd78059 	    space, index, value);
8401c42de6dSgd78059 
8411c42de6dSgd78059 	i2bsc_clear_acc_fault(i2c);
8421c42de6dSgd78059 
8431c42de6dSgd78059 	/*
8441c42de6dSgd78059 	 * If a session failure has already occurred, reduce the level of
8451c42de6dSgd78059 	 * retries to a minimum.  This is a optimization of the failure
8461c42de6dSgd78059 	 * recovery strategy.
8471c42de6dSgd78059 	 */
8481c42de6dSgd78059 	if (i2c->bscbus_session_failure)
8491c42de6dSgd78059 		retryable = 1;
8501c42de6dSgd78059 
8511c42de6dSgd78059 	while (retryable--) {
8521c42de6dSgd78059 		ddi_put8(i2c->bscbus_handle,
8531c42de6dSgd78059 		    I2BSC_NEXUS_ADDR(i2c, space, index), value);
8541c42de6dSgd78059 		if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS) {
8551c42de6dSgd78059 			i2bsc_clear_acc_fault(i2c);
8561c42de6dSgd78059 		} else
8571c42de6dSgd78059 			break;
8581c42de6dSgd78059 	}
8591c42de6dSgd78059 
8601c42de6dSgd78059 	if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
8611c42de6dSgd78059 		i2bsc_fail_session(i2c);
8621c42de6dSgd78059 
8631c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_put8", "tried %d time(s)",
8641c42de6dSgd78059 	    I2BSC_RETRY_LIMIT - retryable);
8651c42de6dSgd78059 }
8661c42de6dSgd78059 
8671c42de6dSgd78059 static uint8_t
8681c42de6dSgd78059 i2bsc_get8(i2bsc_t *i2c, uint8_t space, uint8_t index)
8691c42de6dSgd78059 {
8701c42de6dSgd78059 	uint8_t value;
8711c42de6dSgd78059 	int retryable = I2BSC_RETRY_LIMIT;
8721c42de6dSgd78059 
8731c42de6dSgd78059 	i2bsc_clear_acc_fault(i2c);
8741c42de6dSgd78059 
8751c42de6dSgd78059 	/*
8761c42de6dSgd78059 	 * If a session failure has already occurred, reduce the level of
8771c42de6dSgd78059 	 * retries to a minimum.  This is a optimization of the failure
8781c42de6dSgd78059 	 * recovery strategy.
8791c42de6dSgd78059 	 */
8801c42de6dSgd78059 	if (i2c->bscbus_session_failure)
8811c42de6dSgd78059 		retryable = 1;
8821c42de6dSgd78059 
8831c42de6dSgd78059 	while (retryable--) {
8841c42de6dSgd78059 		value = ddi_get8(i2c->bscbus_handle,
8851c42de6dSgd78059 		    I2BSC_NEXUS_ADDR(i2c, space, index));
8861c42de6dSgd78059 		if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS) {
8871c42de6dSgd78059 			i2bsc_clear_acc_fault(i2c);
8881c42de6dSgd78059 		} else
8891c42de6dSgd78059 			break;
8901c42de6dSgd78059 	}
8911c42de6dSgd78059 
8921c42de6dSgd78059 	if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
8931c42de6dSgd78059 		i2bsc_fail_session(i2c);
8941c42de6dSgd78059 
8951c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_get8", "tried %d time(s)",
8961c42de6dSgd78059 	    I2BSC_RETRY_LIMIT - retryable);
8971c42de6dSgd78059 
8981c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_get8", "(space,index)->val (%d,%d)->%d",
8991c42de6dSgd78059 	    space, index, value);
9001c42de6dSgd78059 	return (value);
9011c42de6dSgd78059 }
9021c42de6dSgd78059 
9031c42de6dSgd78059 static void
9041c42de6dSgd78059 i2bsc_put8_once(i2bsc_t *i2c, uint8_t space, uint8_t index, uint8_t value)
9051c42de6dSgd78059 {
9061c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_put8_once",
9071c42de6dSgd78059 	    "(space,index)<-val (%d,%d)<-%d", space, index, value);
9081c42de6dSgd78059 
9091c42de6dSgd78059 	i2bsc_clear_acc_fault(i2c);
9101c42de6dSgd78059 
9111c42de6dSgd78059 	ddi_put8(i2c->bscbus_handle,
9121c42de6dSgd78059 	    I2BSC_NEXUS_ADDR(i2c, space, index), value);
9131c42de6dSgd78059 
9141c42de6dSgd78059 	if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
9151c42de6dSgd78059 		i2bsc_fail_session(i2c);
9161c42de6dSgd78059 }
9171c42de6dSgd78059 
9181c42de6dSgd78059 static uint8_t
9191c42de6dSgd78059 i2bsc_get8_once(i2bsc_t *i2c, uint8_t space, uint8_t index)
9201c42de6dSgd78059 {
9211c42de6dSgd78059 	uint8_t value;
9221c42de6dSgd78059 
9231c42de6dSgd78059 	i2bsc_clear_acc_fault(i2c);
9241c42de6dSgd78059 
9251c42de6dSgd78059 	value = ddi_get8(i2c->bscbus_handle,
9261c42de6dSgd78059 	    I2BSC_NEXUS_ADDR(i2c, space, index));
9271c42de6dSgd78059 
9281c42de6dSgd78059 	if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
9291c42de6dSgd78059 		i2bsc_fail_session(i2c);
9301c42de6dSgd78059 
9311c42de6dSgd78059 	i2bsc_trace(i2c, '@', "i2bsc_get8_once",
9321c42de6dSgd78059 	    "(space,index)->val (%d,%d)->%d", space, index, value);
9331c42de6dSgd78059 
9341c42de6dSgd78059 	return (value);
9351c42de6dSgd78059 }
9361c42de6dSgd78059 
9371c42de6dSgd78059 static int
9381c42de6dSgd78059 i2bsc_notify_max_transfer_size(i2bsc_t *i2c)
9391c42de6dSgd78059 {
9401c42de6dSgd78059 	/*
9411c42de6dSgd78059 	 * If the underlying hardware does not support the i2c service and
9421c42de6dSgd78059 	 * we are not running in fake_mode, then we cannot set the
9431c42de6dSgd78059 	 * MAX_TRANSFER_SZ.
9441c42de6dSgd78059 	 */
9451c42de6dSgd78059 	if (i2c->i2c_proxy_support == 0)
9461c42de6dSgd78059 		return (DDI_FAILURE);
9471c42de6dSgd78059 
9481c42de6dSgd78059 	i2bsc_start_session(i2c);
9491c42de6dSgd78059 
9501c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_MAX_TRANSFER_SZ,
9511c42de6dSgd78059 	    I2BSC_MAX_TRANSFER_SZ);
9521c42de6dSgd78059 
9531c42de6dSgd78059 	if (i2bsc_end_session(i2c) != DDI_SUCCESS)
9541c42de6dSgd78059 		return (DDI_FAILURE);
9551c42de6dSgd78059 
9561c42de6dSgd78059 	return (DDI_SUCCESS);
9571c42de6dSgd78059 }
9581c42de6dSgd78059 
9591c42de6dSgd78059 /*
9601c42de6dSgd78059  * Discover if the microcontroller implements the I2C Proxy Service this
9611c42de6dSgd78059  * driver requires.  If it does not, i2c transactions will abort with
9621c42de6dSgd78059  * I2C_FAILURE, unless fake_mode is being used.
9631c42de6dSgd78059  */
9641c42de6dSgd78059 static int
9651c42de6dSgd78059 i2bsc_discover_capability(i2bsc_t *i2c)
9661c42de6dSgd78059 {
9671c42de6dSgd78059 	i2bsc_start_session(i2c);
9681c42de6dSgd78059 
9691c42de6dSgd78059 	i2c->i2c_proxy_support = i2bsc_get8(i2c, EBUS_CMD_SPACE_GENERIC,
9701c42de6dSgd78059 	    EBUS_IDX_CAP0);
9711c42de6dSgd78059 	i2c->i2c_proxy_support &= EBUS_CAP0_I2C_PROXY;
9721c42de6dSgd78059 
9731c42de6dSgd78059 	if (i2bsc_end_session(i2c) != DDI_SUCCESS)
9741c42de6dSgd78059 		return (DDI_FAILURE);
9751c42de6dSgd78059 
9761c42de6dSgd78059 	return (DDI_SUCCESS);
9771c42de6dSgd78059 }
9781c42de6dSgd78059 
9791c42de6dSgd78059 static int
9801c42de6dSgd78059 i2bsc_upload_preamble(i2bsc_t *i2c, i2c_transfer_t *tp)
9811c42de6dSgd78059 {
9821c42de6dSgd78059 	i2bsc_ppvt_t *ppvt;
9831c42de6dSgd78059 	int wr_rd;
9841c42de6dSgd78059 
9851c42de6dSgd78059 	ppvt = ddi_get_parent_data(i2c->i2bsc_cur_dip);
9861c42de6dSgd78059 
9871c42de6dSgd78059 	/* Get a lock on the i2c devices owned by the microcontroller */
9881c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_TRANSACTION_LOCK, 1);
9891c42de6dSgd78059 	if (!i2bsc_get8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_TRANSACTION_LOCK)) {
9901c42de6dSgd78059 		/*
9911c42de6dSgd78059 		 * i2c client driver must timeout retry, NOT this nexus
9921c42de6dSgd78059 		 */
9931c42de6dSgd78059 		tp->i2c_result = I2C_INCOMPLETE;
9941c42de6dSgd78059 		i2bsc_trace(i2c, 'U', "i2bsc_upload_preamble",
9951c42de6dSgd78059 		    "Couldn't get transaction lock");
9961c42de6dSgd78059 		return (tp->i2c_result);
9971c42de6dSgd78059 	}
9981c42de6dSgd78059 
9991c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_BUS_ADDRESS,
10001c42de6dSgd78059 	    ppvt->i2bsc_ppvt_bus);
10011c42de6dSgd78059 
10021c42de6dSgd78059 	/*
10031c42de6dSgd78059 	 * The Solaris architecture for I2C uses 10-bit I2C addresses where
10041c42de6dSgd78059 	 * bit-0 is zero (the read/write bit).  The microcontroller uses 7 bit
10051c42de6dSgd78059 	 * I2C addresses (read/write bit excluded).  Hence we need to convert
10061c42de6dSgd78059 	 * the address by bit-shifting.
10071c42de6dSgd78059 	 */
10081c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_CLIENT_ADDRESS,
10091c42de6dSgd78059 	    ppvt->i2bsc_ppvt_addr >> 1);
10101c42de6dSgd78059 
10111c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_TRANSFER_TYPE,
10121c42de6dSgd78059 	    tp->i2c_flags);
10131c42de6dSgd78059 
10141c42de6dSgd78059 	/*
10151c42de6dSgd78059 	 * We have only one register used for data input and output.  When
10161c42de6dSgd78059 	 * a WR_RD is issued, this means we want to do a Random-Access-Read.
10171c42de6dSgd78059 	 * First a series of bytes are written which define the address to
10181c42de6dSgd78059 	 * read from.  In hardware this sets an address pointer.  Then a series
10191c42de6dSgd78059 	 * of bytes are read.  The read/write boundary tells you how many
10201c42de6dSgd78059 	 * bytes are to be written before reads will be issued.
10211c42de6dSgd78059 	 */
10221c42de6dSgd78059 	if (tp->i2c_flags == I2C_WR_RD)
10231c42de6dSgd78059 		wr_rd = tp->i2c_wlen;
10241c42de6dSgd78059 	else
10251c42de6dSgd78059 		wr_rd = 0;
10261c42de6dSgd78059 
10271c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_WR_RD_BOUNDARY, wr_rd);
10281c42de6dSgd78059 
10291c42de6dSgd78059 	return (I2C_SUCCESS);
10301c42de6dSgd78059 }
10311c42de6dSgd78059 
10321c42de6dSgd78059 /*
10331c42de6dSgd78059  * Function	i2bsc_upload
10341c42de6dSgd78059  *
10351c42de6dSgd78059  * Description	This function runs the i2c transfer protocol down to the
10361c42de6dSgd78059  *		microcontroller.  Its goal is to be as reliable as possible.
10371c42de6dSgd78059  *		This is achieved by making all the state-less aspects
10381c42de6dSgd78059  *		re-tryable.  For stateful aspects, we take care to ensure the
10391c42de6dSgd78059  *		counters are decremented only when data transfer has been
10401c42de6dSgd78059  *		successful.
10411c42de6dSgd78059  */
10421c42de6dSgd78059 static int
10431c42de6dSgd78059 i2bsc_upload(i2bsc_t *i2c, i2c_transfer_t *tp)
10441c42de6dSgd78059 {
10451c42de6dSgd78059 	int quota = I2BSC_MAX_TRANSFER_SZ;
10461c42de6dSgd78059 	uint8_t res;
10471c42de6dSgd78059 	int residual;
10481c42de6dSgd78059 
10491c42de6dSgd78059 	/*
10501c42de6dSgd78059 	 * Total amount of data outstanding
10511c42de6dSgd78059 	 */
10521c42de6dSgd78059 	residual = tp->i2c_w_resid + tp->i2c_r_resid;
10531c42de6dSgd78059 
10541c42de6dSgd78059 	/*
10551c42de6dSgd78059 	 * Anything in this session *could* be re-tried without side-effects.
10561c42de6dSgd78059 	 * Therefore, error exit codes are I2C_INCOMPLETE rather than
10571c42de6dSgd78059 	 * I2C_FAILURE.
10581c42de6dSgd78059 	 */
10591c42de6dSgd78059 	i2bsc_start_session(i2c);
10601c42de6dSgd78059 	if (i2bsc_upload_preamble(i2c, tp) != I2C_SUCCESS)
10611c42de6dSgd78059 		return (I2C_INCOMPLETE);
10621c42de6dSgd78059 	if (i2bsc_end_session(i2c) != DDI_SUCCESS)
10631c42de6dSgd78059 		return (I2C_INCOMPLETE);
10641c42de6dSgd78059 
10651c42de6dSgd78059 	/* The writes done here are not retryable */
10661c42de6dSgd78059 	while (tp->i2c_w_resid && quota) {
10671c42de6dSgd78059 		i2bsc_put8_once(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_DATA_INOUT,
10681c42de6dSgd78059 		    tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid]);
10691c42de6dSgd78059 		if (i2bsc_bscbus_state(i2c) == DDI_SUCCESS) {
10701c42de6dSgd78059 			tp->i2c_w_resid--;
10711c42de6dSgd78059 			quota--;
10721c42de6dSgd78059 			residual--;
10731c42de6dSgd78059 		} else {
10741c42de6dSgd78059 			i2bsc_trace(i2c, 'T', "i2bsc_upload", "write failed");
10751c42de6dSgd78059 			return (tp->i2c_result = I2C_INCOMPLETE);
10761c42de6dSgd78059 		}
10771c42de6dSgd78059 	}
10781c42de6dSgd78059 
10791c42de6dSgd78059 	/* The reads done here are not retryable */
10801c42de6dSgd78059 	while (tp->i2c_r_resid && quota) {
10811c42de6dSgd78059 		tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] =
10821c42de6dSgd78059 		    i2bsc_get8_once(i2c, EBUS_CMD_SPACE_I2C,
10831c42de6dSgd78059 		    EBUS_IDX12_DATA_INOUT);
10841c42de6dSgd78059 		if (i2bsc_bscbus_state(i2c) == DDI_SUCCESS) {
10851c42de6dSgd78059 			tp->i2c_r_resid--;
10861c42de6dSgd78059 			quota--;
10871c42de6dSgd78059 			residual--;
10881c42de6dSgd78059 		} else {
10891c42de6dSgd78059 			i2bsc_trace(i2c, 'T', "i2bsc_upload", "read failed");
10901c42de6dSgd78059 			return (tp->i2c_result = I2C_INCOMPLETE);
10911c42de6dSgd78059 		}
10921c42de6dSgd78059 	}
10931c42de6dSgd78059 
10941c42de6dSgd78059 	i2bsc_start_session(i2c);
10951c42de6dSgd78059 
10961c42de6dSgd78059 	/*
10971c42de6dSgd78059 	 * A possible future enhancement would be to allow early breakout of the
10981c42de6dSgd78059 	 * loops seen above.  In such circumstances, "residual" would be non-
10991c42de6dSgd78059 	 * zero.  This may be useful if we want to support the interruption of
11001c42de6dSgd78059 	 * transfer part way through an i2c_transfer_t.
11011c42de6dSgd78059 	 */
11021c42de6dSgd78059 	i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_RESIDUAL_DATA, residual);
11031c42de6dSgd78059 	res = i2bsc_get8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_RESULT);
11041c42de6dSgd78059 	if (i2bsc_end_session(i2c) != DDI_SUCCESS)
11051c42de6dSgd78059 		return (tp->i2c_result = I2C_INCOMPLETE);
11061c42de6dSgd78059 
11071c42de6dSgd78059 	switch (res) {
11081c42de6dSgd78059 		case EBUS_I2C_SUCCESS:
11091c42de6dSgd78059 		tp->i2c_result = I2C_SUCCESS;
11101c42de6dSgd78059 		break;
11111c42de6dSgd78059 		case EBUS_I2C_FAILURE:
11121c42de6dSgd78059 		/*
11131c42de6dSgd78059 		 * This is rare but possible.  A retry may still fix this
11141c42de6dSgd78059 		 * so lets allow that by returning I2C_INCOMPLETE.
11151c42de6dSgd78059 		 * "hifTxRing still contains 1 bytes" is reported by the
11161c42de6dSgd78059 		 * microcontroller when this return value is seen.
11171c42de6dSgd78059 		 */
11181c42de6dSgd78059 		i2bsc_trace(i2c, 'T', "i2bsc_upload", "EBUS_I2C_FAILURE"
11191c42de6dSgd78059 		    " but returning I2C_INCOMPLETE for possible re-try");
11201c42de6dSgd78059 		tp->i2c_result = I2C_INCOMPLETE;
11211c42de6dSgd78059 		break;
11221c42de6dSgd78059 		case EBUS_I2C_INCOMPLETE:
11231c42de6dSgd78059 		tp->i2c_result = I2C_INCOMPLETE;
11241c42de6dSgd78059 		break;
11251c42de6dSgd78059 		default:
11261c42de6dSgd78059 		tp->i2c_result = I2C_FAILURE;
11271c42de6dSgd78059 	}
11281c42de6dSgd78059 
11291c42de6dSgd78059 	return (tp->i2c_result);
11301c42de6dSgd78059 }
11311c42de6dSgd78059 
11321c42de6dSgd78059 /*
11331c42de6dSgd78059  * Function	i2bsc_safe_upload
11341c42de6dSgd78059  *
11351c42de6dSgd78059  * Description	This function is called "safe"-upload because it attempts to
11361c42de6dSgd78059  *		do transaction re-tries for cases where state is not spoiled
11371c42de6dSgd78059  *		by a transaction-level retry.
11381c42de6dSgd78059  */
11391c42de6dSgd78059 static int
11401c42de6dSgd78059 i2bsc_safe_upload(i2bsc_t *i2c, i2c_transfer_t *tp)
11411c42de6dSgd78059 {
11421c42de6dSgd78059 	int retryable = I2BSC_RETRY_LIMIT;
11431c42de6dSgd78059 	int result;
11441c42de6dSgd78059 
11451c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_safe_upload", "Transaction %s",
11461c42de6dSgd78059 	    (tp->i2c_flags == I2C_WR_RD) ? "retryable" : "single-shot");
11471c42de6dSgd78059 
11481c42de6dSgd78059 	/*
11491c42de6dSgd78059 	 * The only re-tryable transaction type is I2C_WR_RD.  If we don't
11501c42de6dSgd78059 	 * have this we can only use session-based recovery offered by
11511c42de6dSgd78059 	 * i2bsc_upload.
11521c42de6dSgd78059 	 */
11531c42de6dSgd78059 	if (tp->i2c_flags != I2C_WR_RD)
11541c42de6dSgd78059 		return (i2bsc_upload(i2c, tp));
11551c42de6dSgd78059 
11561c42de6dSgd78059 	while (retryable--) {
11571c42de6dSgd78059 		result = i2bsc_upload(i2c, tp);
11581c42de6dSgd78059 		if (result == I2C_INCOMPLETE) {
11591c42de6dSgd78059 			/* Have another go */
11601c42de6dSgd78059 			tp->i2c_r_resid = tp->i2c_rlen;
11611c42de6dSgd78059 			tp->i2c_w_resid = tp->i2c_wlen;
11621c42de6dSgd78059 			tp->i2c_result = I2C_SUCCESS;
11631c42de6dSgd78059 			i2bsc_trace(i2c, 'T', "i2bsc_safe_upload",
11641c42de6dSgd78059 			    "Retried (%d)", I2BSC_RETRY_LIMIT - retryable);
11651c42de6dSgd78059 			continue;
11661c42de6dSgd78059 		} else {
11671c42de6dSgd78059 			i2bsc_trace(i2c, 'T', "i2bsc_safe_upload",
11681c42de6dSgd78059 			    "Exiting while loop on result %d", result);
11691c42de6dSgd78059 			return (result);
11701c42de6dSgd78059 		}
11711c42de6dSgd78059 	}
11721c42de6dSgd78059 
11731c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_safe_upload", "Exiting on %d", result);
11741c42de6dSgd78059 	return (result);
11751c42de6dSgd78059 }
11761c42de6dSgd78059 
11771c42de6dSgd78059 /*
11781c42de6dSgd78059  * Function	i2bsc_transfer
11791c42de6dSgd78059  *
11801c42de6dSgd78059  * Description	This is the entry-point that clients use via the Solaris i2c
11811c42de6dSgd78059  *		framework.  It kicks off the servicing of i2c transfer requests.
11821c42de6dSgd78059  */
11831c42de6dSgd78059 int
11841c42de6dSgd78059 i2bsc_transfer(dev_info_t *dip, i2c_transfer_t *tp)
11851c42de6dSgd78059 {
11861c42de6dSgd78059 	i2bsc_t *i2c;
11871c42de6dSgd78059 
11881c42de6dSgd78059 	i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state,
11891c42de6dSgd78059 	    ddi_get_instance(ddi_get_parent(dip)));
11901c42de6dSgd78059 
11911c42de6dSgd78059 	i2bsc_acquire(i2c, dip, tp);
11921c42de6dSgd78059 
11931c42de6dSgd78059 	tp->i2c_r_resid = tp->i2c_rlen;
11941c42de6dSgd78059 	tp->i2c_w_resid = tp->i2c_wlen;
11951c42de6dSgd78059 	tp->i2c_result = I2C_SUCCESS;
11961c42de6dSgd78059 
11971c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Transaction i2c_version/flags"
11981c42de6dSgd78059 	    " %d/%d", tp->i2c_version, tp->i2c_flags);
11991c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Transaction buffer rlen/wlen"
12001c42de6dSgd78059 	    " %d/%d", tp->i2c_rlen, tp->i2c_wlen);
12011c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Transaction ptrs wbuf/rbuf"
12021c42de6dSgd78059 	    " %p/%p", tp->i2c_wbuf, tp->i2c_rbuf);
12031c42de6dSgd78059 
12041c42de6dSgd78059 	if (i2c->i2c_proxy_support)
12051c42de6dSgd78059 		(void) i2bsc_safe_upload(i2c, tp);
12061c42de6dSgd78059 	else
12071c42de6dSgd78059 		tp->i2c_result = I2C_FAILURE;
12081c42de6dSgd78059 
12091c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Residual writes/reads"
12101c42de6dSgd78059 	    " %d/%d", tp->i2c_w_resid, tp->i2c_r_resid);
12111c42de6dSgd78059 	i2bsc_trace(i2c, 'T', "i2bsc_transfer", "i2c_result"
12121c42de6dSgd78059 	    " %d", tp->i2c_result);
12131c42de6dSgd78059 
12141c42de6dSgd78059 	i2bsc_release(i2c);
12151c42de6dSgd78059 
12161c42de6dSgd78059 	return (tp->i2c_result);
12171c42de6dSgd78059 }
12181c42de6dSgd78059 
12191c42de6dSgd78059 /*
12201c42de6dSgd78059  *  General utility routines ...
12211c42de6dSgd78059  */
12221c42de6dSgd78059 
12231c42de6dSgd78059 #ifdef DEBUG
12241c42de6dSgd78059 
12251c42de6dSgd78059 static void
12261c42de6dSgd78059 i2bsc_trace(i2bsc_t *ssp, char code, const char *caller,
12271c42de6dSgd78059 	const char *fmt, ...)
12281c42de6dSgd78059 {
12291c42de6dSgd78059 	char buf[256];
12301c42de6dSgd78059 	char *p;
12311c42de6dSgd78059 	va_list va;
12321c42de6dSgd78059 
12331c42de6dSgd78059 	if (ssp->debug & (1 << (code-'@'))) {
12341c42de6dSgd78059 		p = buf;
12351c42de6dSgd78059 		(void) snprintf(p, sizeof (buf) - (p - buf),
12361c42de6dSgd78059 		    "%s/%s: ", ssp->i2bsc_name, caller);
12371c42de6dSgd78059 		p += strlen(p);
12381c42de6dSgd78059 
12391c42de6dSgd78059 		va_start(va, fmt);
12401c42de6dSgd78059 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
12411c42de6dSgd78059 		va_end(va);
12421c42de6dSgd78059 
12431c42de6dSgd78059 		buf[sizeof (buf) - 1] = '\0';
12441c42de6dSgd78059 		(void) strlog(ssp->majornum, ssp->minornum, code, SL_TRACE,
12451c42de6dSgd78059 		    buf);
12461c42de6dSgd78059 	}
12471c42de6dSgd78059 }
12481c42de6dSgd78059 
12491c42de6dSgd78059 #else /* DEBUG */
12501c42de6dSgd78059 
12511c42de6dSgd78059 _NOTE(ARGSUSED(0))
12521c42de6dSgd78059 static void
12531c42de6dSgd78059 i2bsc_trace(i2bsc_t *ssp, char code, const char *caller,
12541c42de6dSgd78059 	const char *fmt, ...)
12551c42de6dSgd78059 {
12561c42de6dSgd78059 }
12571c42de6dSgd78059 
12581c42de6dSgd78059 #endif /* DEBUG */
1259