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