17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*19397407SSherry Moore * Common Development and Distribution License (the "License"). 6*19397407SSherry Moore * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*19397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #include <sys/stat.h> 287c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 297c478bd9Sstevel@tonic-gate #include <sys/open.h> 307c478bd9Sstevel@tonic-gate #include <sys/types.h> 317c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 327c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 337c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 347c478bd9Sstevel@tonic-gate #include <sys/conf.h> 357c478bd9Sstevel@tonic-gate #include <sys/file.h> 367c478bd9Sstevel@tonic-gate #include <sys/note.h> 377c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h> 387c478bd9Sstevel@tonic-gate #include <sys/i2c/clients/seeprom_impl.h> 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate /* 417c478bd9Sstevel@tonic-gate * cb ops 427c478bd9Sstevel@tonic-gate */ 437c478bd9Sstevel@tonic-gate static int seeprom_open(dev_t *, int, int, cred_t *); 447c478bd9Sstevel@tonic-gate static int seeprom_close(dev_t, int, int, cred_t *); 457c478bd9Sstevel@tonic-gate static int seeprom_read(dev_t, struct uio *, cred_t *); 467c478bd9Sstevel@tonic-gate static int seeprom_write(dev_t, struct uio *, cred_t *); 477c478bd9Sstevel@tonic-gate static int seeprom_io(dev_t, struct uio *, int); 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate /* 507c478bd9Sstevel@tonic-gate * dev ops 517c478bd9Sstevel@tonic-gate */ 527c478bd9Sstevel@tonic-gate static int seeprom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 537c478bd9Sstevel@tonic-gate static int seeprom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 547c478bd9Sstevel@tonic-gate static int seeprom_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate static struct cb_ops seeprom_cbops = { 577c478bd9Sstevel@tonic-gate seeprom_open, /* open */ 587c478bd9Sstevel@tonic-gate seeprom_close, /* close */ 597c478bd9Sstevel@tonic-gate nodev, /* strategy */ 607c478bd9Sstevel@tonic-gate nodev, /* print */ 617c478bd9Sstevel@tonic-gate nodev, /* dump */ 627c478bd9Sstevel@tonic-gate seeprom_read, /* read */ 637c478bd9Sstevel@tonic-gate seeprom_write, /* write */ 647c478bd9Sstevel@tonic-gate nodev, /* ioctl */ 657c478bd9Sstevel@tonic-gate nodev, /* devmap */ 667c478bd9Sstevel@tonic-gate nodev, /* mmap */ 677c478bd9Sstevel@tonic-gate nodev, /* segmap */ 687c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 697c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 707c478bd9Sstevel@tonic-gate NULL, /* streamtab */ 717c478bd9Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 727c478bd9Sstevel@tonic-gate CB_REV, /* rev */ 737c478bd9Sstevel@tonic-gate nodev, /* int (*cb_aread)() */ 747c478bd9Sstevel@tonic-gate nodev /* int (*cb_awrite)() */ 757c478bd9Sstevel@tonic-gate }; 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate static struct dev_ops seeprom_ops = { 787c478bd9Sstevel@tonic-gate DEVO_REV, 797c478bd9Sstevel@tonic-gate 0, 807c478bd9Sstevel@tonic-gate seeprom_info, 817c478bd9Sstevel@tonic-gate nulldev, 827c478bd9Sstevel@tonic-gate nulldev, 837c478bd9Sstevel@tonic-gate seeprom_attach, 847c478bd9Sstevel@tonic-gate seeprom_detach, 857c478bd9Sstevel@tonic-gate nodev, 867c478bd9Sstevel@tonic-gate &seeprom_cbops, 878bc98dc3Sec158148 NULL, 88*19397407SSherry Moore nulldev, 89*19397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 907c478bd9Sstevel@tonic-gate }; 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate static struct modldrv seeprom_modldrv = { 937c478bd9Sstevel@tonic-gate &mod_driverops, /* type of module - driver */ 94*19397407SSherry Moore "I2C serial EEPROM device driver", 957c478bd9Sstevel@tonic-gate &seeprom_ops, 967c478bd9Sstevel@tonic-gate }; 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate static struct modlinkage seeprom_modlinkage = { 997c478bd9Sstevel@tonic-gate MODREV_1, 1007c478bd9Sstevel@tonic-gate &seeprom_modldrv, 1017c478bd9Sstevel@tonic-gate 0 1027c478bd9Sstevel@tonic-gate }; 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate /* 1057c478bd9Sstevel@tonic-gate * globals 1067c478bd9Sstevel@tonic-gate */ 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate static void *seepromsoft_statep; 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate int 1117c478bd9Sstevel@tonic-gate _init(void) 1127c478bd9Sstevel@tonic-gate { 1137c478bd9Sstevel@tonic-gate int error; 1147c478bd9Sstevel@tonic-gate 1158bc98dc3Sec158148 if ((error = ddi_soft_state_init(&seepromsoft_statep, 1168bc98dc3Sec158148 sizeof (struct seepromunit), 1)) != 0) 1178bc98dc3Sec158148 return (error); 1188bc98dc3Sec158148 1198bc98dc3Sec158148 if ((error = mod_install(&seeprom_modlinkage)) != 0) { 1208bc98dc3Sec158148 ddi_soft_state_fini(&seepromsoft_statep); 1218bc98dc3Sec158148 return (error); 1227c478bd9Sstevel@tonic-gate } 1237c478bd9Sstevel@tonic-gate 1247c478bd9Sstevel@tonic-gate return (error); 1257c478bd9Sstevel@tonic-gate } 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate int 1287c478bd9Sstevel@tonic-gate _fini(void) 1297c478bd9Sstevel@tonic-gate { 1307c478bd9Sstevel@tonic-gate int error; 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate error = mod_remove(&seeprom_modlinkage); 1337c478bd9Sstevel@tonic-gate if (error == 0) { 1347c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&seepromsoft_statep); 1357c478bd9Sstevel@tonic-gate } 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate return (error); 1387c478bd9Sstevel@tonic-gate } 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate int 1417c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 1427c478bd9Sstevel@tonic-gate { 1437c478bd9Sstevel@tonic-gate return (mod_info(&seeprom_modlinkage, modinfop)); 1447c478bd9Sstevel@tonic-gate } 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate static int 1477c478bd9Sstevel@tonic-gate seeprom_do_attach(dev_info_t *dip) 1487c478bd9Sstevel@tonic-gate { 1497c478bd9Sstevel@tonic-gate struct seepromunit *unitp; 1507c478bd9Sstevel@tonic-gate int instance; 1517c478bd9Sstevel@tonic-gate dev_t dev; 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(seepromsoft_statep, instance) != 0) { 1567c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s_%d: failed to zalloc softstate", 1577c478bd9Sstevel@tonic-gate ddi_node_name(dip), instance); 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate unitp = ddi_get_soft_state(seepromsoft_statep, instance); 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate unitp->seeprom_dip = dip; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate (void) snprintf(unitp->seeprom_name, sizeof (unitp->seeprom_name), 1677c478bd9Sstevel@tonic-gate "%s%d", ddi_driver_name(dip), instance); 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, ddi_node_name(dip), S_IFCHR, 1707c478bd9Sstevel@tonic-gate instance, SEEPROM_NODE_TYPE, NULL) == DDI_FAILURE) { 1717c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s ddi_create_minor_node failed for '%s'", 1727c478bd9Sstevel@tonic-gate unitp->seeprom_name, ddi_node_name(dip)); 1737c478bd9Sstevel@tonic-gate ddi_soft_state_free(seepromsoft_statep, instance); 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate if (i2c_client_register(dip, &unitp->seeprom_hdl) != I2C_SUCCESS) { 1797c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i2c_client_register failed\n"); 1807c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 1817c478bd9Sstevel@tonic-gate ddi_soft_state_free(seepromsoft_statep, instance); 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate if (strcmp(ddi_binding_name(dip), "i2c-at34c02") == 0) { 1877c478bd9Sstevel@tonic-gate unitp->seeprom_addrsize = AT34C02_ADDRSIZE; 1887c478bd9Sstevel@tonic-gate unitp->seeprom_memsize = AT34C02_MEMSIZE; 1897c478bd9Sstevel@tonic-gate unitp->seeprom_pagesize = AT34C02_PAGESIZE; 1907c478bd9Sstevel@tonic-gate unitp->seeprom_pagemask = AT34C02_PAGEMASK; 1917c478bd9Sstevel@tonic-gate } else { 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * Default is i2c-at24c64 1947c478bd9Sstevel@tonic-gate */ 1957c478bd9Sstevel@tonic-gate unitp->seeprom_addrsize = AT24C64_ADDRSIZE; 1967c478bd9Sstevel@tonic-gate unitp->seeprom_memsize = AT24C64_MEMSIZE; 1977c478bd9Sstevel@tonic-gate unitp->seeprom_pagesize = AT24C64_PAGESIZE; 1987c478bd9Sstevel@tonic-gate unitp->seeprom_pagemask = AT24C64_PAGEMASK; 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate dev = makedevice(DDI_MAJOR_T_UNKNOWN, instance); 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate (void) ddi_prop_create(dev, dip, DDI_PROP_CANSLEEP, "size", 2037c478bd9Sstevel@tonic-gate (caddr_t)&unitp->seeprom_memsize, sizeof (unitp->seeprom_memsize)); 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate mutex_init(&unitp->seeprom_mutex, NULL, MUTEX_DRIVER, NULL); 2067c478bd9Sstevel@tonic-gate cv_init(&unitp->seeprom_cv, NULL, CV_DRIVER, NULL); 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2097c478bd9Sstevel@tonic-gate } 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate static int 2127c478bd9Sstevel@tonic-gate seeprom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2137c478bd9Sstevel@tonic-gate { 2147c478bd9Sstevel@tonic-gate switch (cmd) { 2157c478bd9Sstevel@tonic-gate case DDI_ATTACH: 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate return (seeprom_do_attach(dip)); 2187c478bd9Sstevel@tonic-gate case DDI_RESUME: 2197c478bd9Sstevel@tonic-gate /* 2207c478bd9Sstevel@tonic-gate * No state to restore. 2217c478bd9Sstevel@tonic-gate */ 2227c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2237c478bd9Sstevel@tonic-gate default: 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate } 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate static int 2307c478bd9Sstevel@tonic-gate seeprom_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 2317c478bd9Sstevel@tonic-gate { 2327c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(dip)) 2337c478bd9Sstevel@tonic-gate struct seepromunit *unitp; 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate switch (infocmd) { 2367c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 2377c478bd9Sstevel@tonic-gate unitp = ddi_get_soft_state(seepromsoft_statep, 2387c478bd9Sstevel@tonic-gate getminor((dev_t)arg)); 2397c478bd9Sstevel@tonic-gate if (unitp == NULL) { 2407c478bd9Sstevel@tonic-gate 2418bc98dc3Sec158148 return (DDI_FAILURE); 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate *result = (void *)unitp->seeprom_dip; 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2467c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 247f47a9c50Smathue *result = (void *)(uintptr_t)getminor((dev_t)arg); 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2507c478bd9Sstevel@tonic-gate default: 2517c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2527c478bd9Sstevel@tonic-gate } 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate } 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate static int 2577c478bd9Sstevel@tonic-gate seeprom_do_detach(dev_info_t *dip) 2587c478bd9Sstevel@tonic-gate { 2597c478bd9Sstevel@tonic-gate struct seepromunit *unitp; 2607c478bd9Sstevel@tonic-gate int instance; 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 2637c478bd9Sstevel@tonic-gate unitp = ddi_get_soft_state(seepromsoft_statep, instance); 2647c478bd9Sstevel@tonic-gate i2c_client_unregister(unitp->seeprom_hdl); 2657c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 2667c478bd9Sstevel@tonic-gate mutex_destroy(&unitp->seeprom_mutex); 2677c478bd9Sstevel@tonic-gate cv_destroy(&unitp->seeprom_cv); 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate ddi_soft_state_free(seepromsoft_statep, instance); 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2727c478bd9Sstevel@tonic-gate } 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate static int 2757c478bd9Sstevel@tonic-gate seeprom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 2767c478bd9Sstevel@tonic-gate { 2777c478bd9Sstevel@tonic-gate switch (cmd) { 2787c478bd9Sstevel@tonic-gate case DDI_DETACH: 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate return (seeprom_do_detach(dip)); 2817c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 2827c478bd9Sstevel@tonic-gate /* 2837c478bd9Sstevel@tonic-gate * No state to save. IO will be blocked by nexus. 2847c478bd9Sstevel@tonic-gate */ 2857c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2867c478bd9Sstevel@tonic-gate default: 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2897c478bd9Sstevel@tonic-gate } 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate static int 2937c478bd9Sstevel@tonic-gate seeprom_open(dev_t *devp, int flags, int otyp, cred_t *credp) 2947c478bd9Sstevel@tonic-gate { 2957c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(credp)) 2967c478bd9Sstevel@tonic-gate struct seepromunit *unitp; 2977c478bd9Sstevel@tonic-gate int instance; 2987c478bd9Sstevel@tonic-gate int err = 0; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) { 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate return (EINVAL); 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate instance = getminor(*devp); 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate unitp = (struct seepromunit *) 3087c478bd9Sstevel@tonic-gate ddi_get_soft_state(seepromsoft_statep, instance); 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate if (unitp == NULL) { 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate return (ENXIO); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate mutex_enter(&unitp->seeprom_mutex); 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate if (flags & FEXCL) { 3187c478bd9Sstevel@tonic-gate if (unitp->seeprom_oflag != 0) { 3197c478bd9Sstevel@tonic-gate err = EBUSY; 3207c478bd9Sstevel@tonic-gate } else { 3217c478bd9Sstevel@tonic-gate unitp->seeprom_oflag = FEXCL; 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate } else { 3247c478bd9Sstevel@tonic-gate if (unitp->seeprom_oflag == FEXCL) { 3257c478bd9Sstevel@tonic-gate err = EBUSY; 3267c478bd9Sstevel@tonic-gate } else { 3277c478bd9Sstevel@tonic-gate unitp->seeprom_oflag = FOPEN; 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate mutex_exit(&unitp->seeprom_mutex); 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate return (err); 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate static int 3377c478bd9Sstevel@tonic-gate seeprom_close(dev_t dev, int flags, int otyp, cred_t *credp) 3387c478bd9Sstevel@tonic-gate { 3397c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(flags, otyp, credp)) 3407c478bd9Sstevel@tonic-gate struct seepromunit *unitp; 3417c478bd9Sstevel@tonic-gate int instance; 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate instance = getminor(dev); 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate unitp = (struct seepromunit *) 3467c478bd9Sstevel@tonic-gate ddi_get_soft_state(seepromsoft_statep, instance); 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate if (unitp == NULL) { 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate return (ENXIO); 3517c478bd9Sstevel@tonic-gate } 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate mutex_enter(&unitp->seeprom_mutex); 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate unitp->seeprom_oflag = 0; 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate mutex_exit(&unitp->seeprom_mutex); 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate static int 3637c478bd9Sstevel@tonic-gate seeprom_read(dev_t dev, struct uio *uiop, cred_t *cred_p) 3647c478bd9Sstevel@tonic-gate { 3657c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(cred_p)) 3667c478bd9Sstevel@tonic-gate return (seeprom_io(dev, uiop, B_READ)); 3677c478bd9Sstevel@tonic-gate } 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate static int 3707c478bd9Sstevel@tonic-gate seeprom_write(dev_t dev, struct uio *uiop, cred_t *cred_p) 3717c478bd9Sstevel@tonic-gate { 3727c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(cred_p)) 3737c478bd9Sstevel@tonic-gate return (seeprom_io(dev, uiop, B_WRITE)); 3747c478bd9Sstevel@tonic-gate } 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate static int 3777c478bd9Sstevel@tonic-gate seeprom_io(dev_t dev, struct uio *uiop, int rw) 3787c478bd9Sstevel@tonic-gate { 3797c478bd9Sstevel@tonic-gate struct seepromunit *unitp; 3807c478bd9Sstevel@tonic-gate int instance = getminor(dev); 3817c478bd9Sstevel@tonic-gate int seeprom_addr; 3827c478bd9Sstevel@tonic-gate int bytes_to_rw; 3837c478bd9Sstevel@tonic-gate int err = 0; 3847c478bd9Sstevel@tonic-gate int current_xfer_len; 3857c478bd9Sstevel@tonic-gate int actual_data_xfer; 3867c478bd9Sstevel@tonic-gate i2c_transfer_t *i2ctp = NULL; 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate unitp = (struct seepromunit *) 3897c478bd9Sstevel@tonic-gate ddi_get_soft_state(seepromsoft_statep, instance); 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate if (unitp == NULL) { 3937c478bd9Sstevel@tonic-gate return (ENXIO); 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate if (uiop->uio_offset >= unitp->seeprom_memsize) { 3977c478bd9Sstevel@tonic-gate /* 3987c478bd9Sstevel@tonic-gate * Exceeded seeprom size. 3997c478bd9Sstevel@tonic-gate */ 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate return (ENXIO); 4027c478bd9Sstevel@tonic-gate } 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate seeprom_addr = uiop->uio_offset; 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate if (uiop->uio_resid == 0) { 4077c478bd9Sstevel@tonic-gate return (0); 4087c478bd9Sstevel@tonic-gate } 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate bytes_to_rw = min(uiop->uio_resid, 4117c478bd9Sstevel@tonic-gate unitp->seeprom_memsize - uiop->uio_offset); 4127c478bd9Sstevel@tonic-gate /* 4137c478bd9Sstevel@tonic-gate * Serialize access here to prevent a transaction starting 4147c478bd9Sstevel@tonic-gate * until after 20 ms delay if last operation was a write. 4157c478bd9Sstevel@tonic-gate */ 4167c478bd9Sstevel@tonic-gate mutex_enter(&unitp->seeprom_mutex); 4177c478bd9Sstevel@tonic-gate while ((unitp->seeprom_flags & SEEPROM_BUSY) == SEEPROM_BUSY) { 4187c478bd9Sstevel@tonic-gate if (cv_wait_sig(&unitp->seeprom_cv, 4197c478bd9Sstevel@tonic-gate &unitp->seeprom_mutex) <= 0) { 4207c478bd9Sstevel@tonic-gate mutex_exit(&unitp->seeprom_mutex); 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate return (EINTR); 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate unitp->seeprom_flags |= SEEPROM_BUSY; 4267c478bd9Sstevel@tonic-gate mutex_exit(&unitp->seeprom_mutex); 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate while ((bytes_to_rw != 0) && (err == 0)) { 4297c478bd9Sstevel@tonic-gate current_xfer_len = min(bytes_to_rw, unitp->seeprom_pagesize - 4307c478bd9Sstevel@tonic-gate (seeprom_addr & unitp->seeprom_pagemask)); 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate if (rw == B_WRITE) { 4337c478bd9Sstevel@tonic-gate if (i2ctp == NULL) { 4347c478bd9Sstevel@tonic-gate (void) i2c_transfer_alloc(unitp->seeprom_hdl, 4357c478bd9Sstevel@tonic-gate &i2ctp, 4367c478bd9Sstevel@tonic-gate unitp->seeprom_addrsize + current_xfer_len, 4377c478bd9Sstevel@tonic-gate 0, 4387c478bd9Sstevel@tonic-gate I2C_SLEEP); 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate if ((err = uiomove(&i2ctp->i2c_wbuf[ 4417c478bd9Sstevel@tonic-gate unitp->seeprom_addrsize], 4427c478bd9Sstevel@tonic-gate current_xfer_len, UIO_WRITE, uiop)) != 0) { 4437c478bd9Sstevel@tonic-gate i2c_transfer_free(unitp->seeprom_hdl, 4447c478bd9Sstevel@tonic-gate i2ctp); 4457c478bd9Sstevel@tonic-gate break; 4467c478bd9Sstevel@tonic-gate } 4477c478bd9Sstevel@tonic-gate i2ctp->i2c_version = I2C_XFER_REV; 4487c478bd9Sstevel@tonic-gate i2ctp->i2c_flags = I2C_WR; 4497c478bd9Sstevel@tonic-gate } else { 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate /* 4527c478bd9Sstevel@tonic-gate * not all bytes were sent in previous attempt. 4537c478bd9Sstevel@tonic-gate * Adjust the write pointer to the unsent data. 4547c478bd9Sstevel@tonic-gate */ 4557c478bd9Sstevel@tonic-gate /*LINTED*/ 4567c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf += actual_data_xfer; 4577c478bd9Sstevel@tonic-gate /*LINTED*/ 4587c478bd9Sstevel@tonic-gate i2ctp->i2c_wlen -= actual_data_xfer; 4597c478bd9Sstevel@tonic-gate } 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate if (unitp->seeprom_addrsize == 2) { 4627c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf[0] = (seeprom_addr >> 8); 4637c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf[1] = (uchar_t)seeprom_addr; 4647c478bd9Sstevel@tonic-gate } else { 4657c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf[0] = (uchar_t)seeprom_addr; 4667c478bd9Sstevel@tonic-gate } 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate if ((err = i2c_transfer(unitp->seeprom_hdl, i2ctp)) != 4697c478bd9Sstevel@tonic-gate I2C_SUCCESS) { 4707c478bd9Sstevel@tonic-gate i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 4717c478bd9Sstevel@tonic-gate break; 4727c478bd9Sstevel@tonic-gate } 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate actual_data_xfer = i2ctp->i2c_wlen - 4757c478bd9Sstevel@tonic-gate i2ctp->i2c_w_resid - unitp->seeprom_addrsize; 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate if (i2ctp->i2c_w_resid == 0) { 4787c478bd9Sstevel@tonic-gate i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 4797c478bd9Sstevel@tonic-gate i2ctp = NULL; 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate /* 4827c478bd9Sstevel@tonic-gate * 20 ms(20000 Microsec) delay is required before 4837c478bd9Sstevel@tonic-gate * issuing another transaction. This enforces that 4847c478bd9Sstevel@tonic-gate * wait. 4857c478bd9Sstevel@tonic-gate */ 4867c478bd9Sstevel@tonic-gate delay(drv_usectohz(20000)); 4877c478bd9Sstevel@tonic-gate } else { 4887c478bd9Sstevel@tonic-gate /* 4897c478bd9Sstevel@tonic-gate * SEEPROM read. First write out the address to read. 4907c478bd9Sstevel@tonic-gate */ 4917c478bd9Sstevel@tonic-gate (void) i2c_transfer_alloc(unitp->seeprom_hdl, &i2ctp, 4927c478bd9Sstevel@tonic-gate unitp->seeprom_addrsize, current_xfer_len, 4937c478bd9Sstevel@tonic-gate I2C_SLEEP); 4947c478bd9Sstevel@tonic-gate i2ctp->i2c_version = I2C_XFER_REV; 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate if (unitp->seeprom_addrsize == 2) { 4977c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf[0] = (seeprom_addr >> 8); 4987c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf[1] = (uchar_t)seeprom_addr; 4997c478bd9Sstevel@tonic-gate } else { 5007c478bd9Sstevel@tonic-gate i2ctp->i2c_wbuf[0] = (uchar_t)seeprom_addr; 5017c478bd9Sstevel@tonic-gate } 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate i2ctp->i2c_flags = I2C_WR_RD; 5047c478bd9Sstevel@tonic-gate 5057c478bd9Sstevel@tonic-gate if ((err = i2c_transfer(unitp->seeprom_hdl, i2ctp)) != 5067c478bd9Sstevel@tonic-gate I2C_SUCCESS) { 5077c478bd9Sstevel@tonic-gate i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 5087c478bd9Sstevel@tonic-gate break; 5097c478bd9Sstevel@tonic-gate } 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate actual_data_xfer = i2ctp->i2c_rlen - i2ctp->i2c_r_resid; 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate err = uiomove(i2ctp->i2c_rbuf, actual_data_xfer, 5147c478bd9Sstevel@tonic-gate UIO_READ, uiop); 5157c478bd9Sstevel@tonic-gate i2c_transfer_free(unitp->seeprom_hdl, i2ctp); 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate bytes_to_rw -= actual_data_xfer; 5197c478bd9Sstevel@tonic-gate seeprom_addr += actual_data_xfer; 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate mutex_enter(&unitp->seeprom_mutex); 5237c478bd9Sstevel@tonic-gate unitp->seeprom_flags = 0; 5247c478bd9Sstevel@tonic-gate cv_signal(&unitp->seeprom_cv); 5257c478bd9Sstevel@tonic-gate mutex_exit(&unitp->seeprom_mutex); 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate return (err); 5287c478bd9Sstevel@tonic-gate } 529