diff options
Diffstat (limited to 'usr/src/uts/sun4u/io/pic16f747.c')
| -rw-r--r-- | usr/src/uts/sun4u/io/pic16f747.c | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/io/pic16f747.c b/usr/src/uts/sun4u/io/pic16f747.c new file mode 100644 index 0000000000..f95a223358 --- /dev/null +++ b/usr/src/uts/sun4u/io/pic16f747.c @@ -0,0 +1,496 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Driver to map the PIC for the chicago platform. + */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <sys/cmn_err.h> +#include <sys/param.h> +#include <sys/modctl.h> +#include <sys/conf.h> +#include <sys/open.h> +#include <sys/stat.h> +#include <sys/clock.h> +#include <sys/pic.h> +#include <sys/pic16f747.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/file.h> + +/* dev_ops and cb_ops entry point function declarations */ +static int pic_attach(dev_info_t *, ddi_attach_cmd_t); +static int pic_detach(dev_info_t *, ddi_detach_cmd_t); +static int pic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int pic_open(dev_t *, int, int, cred_t *); +static int pic_close(dev_t, int, int, cred_t *); +static int pic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +struct cb_ops pic_cb_ops = { + pic_open, + pic_close, + nodev, + nodev, + nodev, /* dump */ + nodev, + nodev, + pic_ioctl, + nodev, /* devmap */ + nodev, + ddi_segmap, /* segmap */ + nochpoll, + ddi_prop_op, + NULL, /* for STREAMS drivers */ + D_NEW | D_MP /* driver compatibility flag */ +}; + +static struct dev_ops pic_dev_ops = { + DEVO_REV, /* driver build version */ + 0, /* device reference count */ + pic_getinfo, + nulldev, + nulldev, /* probe */ + pic_attach, + pic_detach, + nulldev, /* reset */ + &pic_cb_ops, + (struct bus_ops *)NULL, + nulldev /* power */ +}; + +/* + * Fans' and sensors' node names and register offsets + */ +static struct minor_node_info pic_nodes[N_PIC_NODES] = { + {NULL, 0, 0}, /* Reserved */ + {"fan_0", RF_FAN0_PERIOD, 0}, /* System Fan 0 */ + {"fan_1", RF_FAN1_PERIOD, 1}, /* System Fan 1 */ + {"fan_2", RF_FAN2_PERIOD, 2}, /* System Fan 2 */ + {"fan_3", RF_FAN3_PERIOD, 3}, /* System Fan 3 */ + {"fan_4", RF_FAN4_PERIOD, 4}, /* System Fan 4 in P0.1 */ + {"adt7462", RF_LOCAL_TEMP, 0}, /* ADT7462 Local Temperature */ + {"cpu_0", RF_REMOTE1_TEMP, 0}, /* CPU 0 temp */ + {"cpu_1", RF_REMOTE2_TEMP, 0}, /* CPU 1 temp */ + {"mb", RF_REMOTE3_TEMP, 0}, /* Motherboard temp */ + {"lm95221", RF_LM95221_TEMP, 0}, /* LM95221 Local Temperature */ + {"fire", RF_FIRE_TEMP, 0}, /* FIRE Temp */ + {"lsi1064", RF_LSI1064_TEMP, 0}, /* LSI1064 Temp */ + {"front_panel", RF_FRONT_TEMP, 0} /* Front Panel Temperature */ +}; + +/* + * Soft state + */ +struct pic_softc { + dev_info_t *dip; + kmutex_t mutex; + uint8_t *cmd_reg; + ddi_acc_handle_t cmd_handle; +}; +#define getsoftc(inst) ((struct pic_softc *)ddi_get_soft_state(statep, (inst))) + +/* module configuration stuff */ +static void *statep; +extern struct mod_ops mod_driverops; + +static struct modldrv modldrv = { + &mod_driverops, + "pic_client driver (v.%I%) ", + &pic_dev_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + &modldrv, + 0 +}; + +int +_init(void) +{ + int e; + + if (e = ddi_soft_state_init(&statep, sizeof (struct pic_softc), + MAX_PIC_INSTANCES)) { + return (e); + } + + if ((e = mod_install(&modlinkage)) != 0) + ddi_soft_state_fini(&statep); + + return (e); +} + +int +_fini(void) +{ + int e; + + if ((e = mod_remove(&modlinkage)) != 0) + return (e); + + ddi_soft_state_fini(&statep); + + return (DDI_SUCCESS); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/*ARGSUSED*/ +static int +pic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + int inst; + int retval = DDI_SUCCESS; + struct pic_softc *softc; + + inst = PIC_MINOR_TO_INST(getminor((dev_t)arg)); + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + if ((softc = getsoftc(inst)) == NULL) { + *result = (void *)NULL; + retval = DDI_FAILURE; + } else + *result = (void *)softc->dip; + break; + + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)inst; + break; + + default: + retval = DDI_FAILURE; + } + + return (retval); +} + +static int +pic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int inst; + int i; + struct pic_softc *softc = NULL; + char *minor_name; + int minor; + char name[80]; + ddi_device_acc_attr_t dev_attr; + int res; + + switch (cmd) { + case DDI_ATTACH: + inst = ddi_get_instance(dip); + if (inst >= MAX_PIC_INSTANCES) { + cmn_err(CE_WARN, "attach failed, too many instances\n"); + return (DDI_FAILURE); + } + + (void) sprintf(name, "env-monitor%d", inst); + minor = PIC_INST_TO_MINOR(inst) | PIC_UNIT_TO_MINOR(0); + if (ddi_create_minor_node(dip, name, S_IFCHR, minor, + DDI_PSEUDO, NULL) == DDI_FAILURE) { + cmn_err(CE_WARN, + "ddi_create_minor_node() failed for inst %d\n", + inst); + return (DDI_FAILURE); + } + + /* Allocate a soft state structure for this instance */ + if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) { + cmn_err(CE_WARN, " ddi_soft_state_zalloc() failed " + "for inst %d\n", inst); + goto attach_failed; + } + + /* Setup soft state */ + softc = getsoftc(inst); + softc->dip = dip; + mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL); + + /* Setup device attributes */ + dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + /* + * The RF_COMMAND/RF_STATUS and RF_IND_DATA/RF_IND_ADDR + * register pairs are mapped as one register set starting + * from 0x0 and length 0x42. + */ + res = ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->cmd_reg, + 0, 0x42, &dev_attr, &softc->cmd_handle); + if (res != DDI_SUCCESS) { + cmn_err(CE_WARN, "ddi_regs_map_setup() failed\n"); + goto attach_failed; + } + + /* Set up fans' and sensors' device minor nodes */ + for (i = 1; i < N_PIC_NODES; i++) { + minor_name = pic_nodes[i].minor_name; + minor = PIC_INST_TO_MINOR(inst) | PIC_UNIT_TO_MINOR(i); + if (ddi_create_minor_node(dip, minor_name, S_IFCHR, + minor, PICDEV_NODE_TYPE, NULL) == DDI_FAILURE) { + cmn_err(CE_WARN, + "%s:%d ddi_create_minor_node failed", + ddi_driver_name(dip), inst); + (void) pic_detach(dip, DDI_DETACH); + return (DDI_FAILURE); + } + } + + /* Create main environmental node */ + ddi_report_dev(dip); + + return (DDI_SUCCESS); + + case DDI_RESUME: + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + +attach_failed: + + /* Free soft state, if allocated. remove minor node if added earlier */ + if (softc) + ddi_soft_state_free(statep, inst); + + ddi_remove_minor_node(dip, NULL); + + return (DDI_FAILURE); +} + +static int +pic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int inst; + struct pic_softc *softc; + + switch (cmd) { + case DDI_DETACH: + inst = ddi_get_instance(dip); + if ((softc = getsoftc(inst)) == NULL) + return (ENXIO); + + (void) ddi_regs_map_free(&softc->cmd_handle); + /* Free the soft state and remove minor node added earlier */ + mutex_destroy(&softc->mutex); + ddi_soft_state_free(statep, inst); + ddi_remove_minor_node(dip, NULL); + return (DDI_SUCCESS); + + case DDI_SUSPEND: + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + +/*ARGSUSED*/ +static int +pic_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + int inst = PIC_MINOR_TO_INST(getminor(*devp)); + + return (getsoftc(inst) == NULL ? ENXIO : 0); +} + +/*ARGSUSED*/ +static int +pic_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + int inst = PIC_MINOR_TO_INST(getminor(dev)); + + return (getsoftc(inst) == NULL ? ENXIO : 0); +} + +/*ARGSUSED*/ +static int +pic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) +{ + int inst; + int node; + int ntries; + struct pic_softc *softc; + uint8_t in_command; + uint8_t status; + int16_t tempr; + + inst = PIC_MINOR_TO_INST(getminor(dev)); + if ((softc = getsoftc(inst)) == NULL) + return (ENXIO); + + mutex_enter(&softc->mutex); + + if (ddi_copyin((caddr_t)arg, &in_command, sizeof (in_command), + mode) != DDI_SUCCESS) { + mutex_exit(&softc->mutex); + return (EFAULT); + } + + node = PIC_MINOR_TO_UNIT(getminor(dev)); + if ((node >= N_PIC_NODES) || (node < 1)) { + mutex_exit(&softc->mutex); + return (ENXIO); + } + + /* Check status register */ + ntries = 0; + do { + if (++ntries > MAX_RETRIES) { + mutex_exit(&softc->mutex); + return (EBUSY); + } + + status = ddi_get8(softc->cmd_handle, + (uint8_t *)softc->cmd_reg + RF_STATUS); + /* + * We need 5us delay between 2 register reads + * this give enough time for the pic to be updated. + * we are waiting 10us to give us some breathing room. + */ + drv_usecwait(10); + + /* + * If we're asked to return status, we simply + * return it as is. + */ + if (cmd == PIC_GET_STATUS) + break; + + } while ((status & ST_ENV_BUSY) || (status & ST_STALE_ADT_DATA) || + (status & ST_STALE_LM_DATA)); + + switch (cmd) { + case PIC_GET_TEMPERATURE: + + /* select the temp sensor */ + (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + + RF_IND_ADDR, pic_nodes[node].reg_offset); + + /* retrieve temperature data */ + tempr = (int16_t)ddi_get8(softc->cmd_handle, + (uint8_t *)softc->cmd_reg + RF_IND_DATA); + + drv_usecwait(10); + + if (tempr == 0xff) { + mutex_exit(&softc->mutex); + return (EIO); + } + + /* + * The temp is passed in as a uint8 value, we need to convert + * it to a signed 16 bit value to be able to handle the range + * of -64 to 190 degrees. + */ + tempr -= 64; + + /* In this case we need to return a signed int value */ + mutex_exit(&softc->mutex); + (void) ddi_copyout(&tempr, (caddr_t)arg, sizeof (tempr), mode); + + return (0); + + case PIC_GET_FAN_SPEED: + /* select fan */ + (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + + RF_IND_ADDR, pic_nodes[node].reg_offset); + + /* retrieve fan data */ + in_command = ddi_get8(softc->cmd_handle, + (uint8_t *)softc->cmd_reg + RF_IND_DATA); + + drv_usecwait(10); + break; + + case PIC_SET_FAN_SPEED: + /* select fan */ + (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + + RF_IND_ADDR, pic_nodes[node].reg_offset); + + /* send the fan data */ + (void) ddi_put8(softc->cmd_handle, + (uint8_t *)softc->cmd_reg + RF_IND_DATA, in_command); + + drv_usecwait(10); + + break; + + case PIC_GET_STATUS: + in_command = status; + break; + + case PIC_GET_FAN_STATUS: + /* read ffault register */ + (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + + RF_IND_ADDR, RF_FAN_STATUS); + + /* retrieve fan failure status */ + in_command = ddi_get8(softc->cmd_handle, + (uint8_t *)softc->cmd_reg + RF_IND_DATA); + in_command = (in_command >> pic_nodes[node].ff_shift) & 0x1; + + drv_usecwait(10); + + break; + + case PIC_SET_ESTAR_MODE: + (void) ddi_put8(softc->cmd_handle, + (uint8_t *)softc->cmd_reg + RF_COMMAND, CMD_TO_ESTAR); + break; + + default: + mutex_exit(&softc->mutex); + cmn_err(CE_NOTE, "cmd %d isnt valid", cmd); + return (EINVAL); + } + + mutex_exit(&softc->mutex); + (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode); + + /* + * 0xFF indicates an error when trying to read from the device, + * ie. it can be busy, or being updated. + * This will force picl to retry the read at another time. + */ + if (in_command == 0xff) { + return (EIO); + } else { + return (0); + } +} |
