summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/io/pic16f747.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/io/pic16f747.c')
-rw-r--r--usr/src/uts/sun4u/io/pic16f747.c496
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);
+ }
+}