summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/grover/io/grfans.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/grover/io/grfans.c')
-rw-r--r--usr/src/uts/sun4u/grover/io/grfans.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/grover/io/grfans.c b/usr/src/uts/sun4u/grover/io/grfans.c
new file mode 100644
index 0000000000..7b03356c2c
--- /dev/null
+++ b/usr/src/uts/sun4u/grover/io/grfans.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/modctl.h>
+#include <sys/open.h>
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/systm.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/conf.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+
+#include <sys/grfans.h>
+
+/*
+ * cb ops
+ */
+static int grfans_open(dev_t *, int, int, cred_t *);
+static int grfans_close(dev_t, int, int, cred_t *);
+static int grfans_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
+static int grfans_write(dev_t dev, struct uio *uiop, cred_t *cred_p);
+static int grfans_io(dev_t dev, struct uio *uiop, int rw);
+/*
+ * dev ops
+ */
+static int grfans_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **result);
+static int grfans_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int grfans_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static struct cb_ops grfans_cbops = {
+ grfans_open, /* open */
+ grfans_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ grfans_read, /* read */
+ grfans_write, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
+ CB_REV, /* rev */
+ nodev, /* int (*cb_aread)() */
+ nodev /* int (*cb_awrite)() */
+};
+
+static struct dev_ops grfans_ops = {
+ DEVO_REV,
+ 0,
+ grfans_info,
+ nulldev,
+ nulldev,
+ grfans_attach,
+ grfans_detach,
+ nodev,
+ &grfans_cbops,
+ NULL
+};
+
+static struct modldrv grfans_modldrv = {
+ &mod_driverops, /* type of module - driver */
+ "grfans device driver v%I%",
+ &grfans_ops,
+};
+
+static struct modlinkage grfans_modlinkage = {
+ MODREV_1,
+ &grfans_modldrv,
+ 0
+};
+
+static void *grfans_soft_statep;
+static int grfans_debug = 0;
+
+int
+_init(void)
+{
+ int error;
+
+ error = mod_install(&grfans_modlinkage);
+ if (error == 0) {
+ (void) ddi_soft_state_init(&grfans_soft_statep,
+ sizeof (struct grfans_unit), 1);
+ }
+
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&grfans_modlinkage);
+ if (error == 0) {
+ ddi_soft_state_fini(&grfans_soft_statep);
+ }
+
+ return (error);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&grfans_modlinkage, modinfop));
+}
+
+/* ARGSUSED */
+static int
+grfans_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ dev_t dev;
+ int instance;
+
+ if (infocmd == DDI_INFO_DEVT2INSTANCE) {
+ dev = (dev_t)arg;
+ instance = MINOR_TO_DEVINST(dev);
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+static int
+grfans_do_attach(dev_info_t *dip)
+{
+ struct grfans_unit *unitp;
+ int instance;
+ ddi_device_acc_attr_t attr;
+ int nregs;
+ char name[32];
+
+ instance = ddi_get_instance(dip);
+
+ if (ddi_soft_state_zalloc(grfans_soft_statep, instance) != 0) {
+ cmn_err(CE_WARN, "%s%d failed to zalloc softstate",
+ ddi_get_name(dip), instance);
+
+ return (DDI_FAILURE);
+ }
+
+ if (grfans_debug) {
+ printf("attached instance number %d\n", instance);
+ }
+
+ unitp = ddi_get_soft_state(grfans_soft_statep, instance);
+ if (unitp == NULL)
+ return (DDI_FAILURE);
+
+ (void) snprintf(name, sizeof (name), "%s%d", ddi_driver_name(dip),
+ instance);
+
+ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+ if (grfans_debug) {
+ printf("number of registers is %d\n",
+ ddi_dev_nregs(dip, &nregs));
+ }
+
+ if (ddi_regs_map_setup(dip, 0,
+ (caddr_t *)&unitp->cpufan_reg,
+ 3, 1, &attr, &unitp->cpufan_rhandle) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s ddi_regs_map_setup failed for regset "
+ "0", name);
+ ddi_soft_state_free(grfans_soft_statep, instance);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_regs_map_setup(dip, 1,
+ (caddr_t *)&unitp->sysfan_reg,
+ 0, 1, &attr, &unitp->sysfan_rhandle) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s ddi_regs_map_setup failed for regset "
+ "1", name);
+ ddi_regs_map_free(&unitp->cpufan_rhandle);
+ ddi_soft_state_free(grfans_soft_statep, instance);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(dip, "cpu_fan", S_IFCHR,
+ DEVINST_TO_MINOR(instance) | CHANNEL_TO_MINOR(CPU_FAN_CHANNEL),
+ FANS_NODE_TYPE, NULL) == DDI_FAILURE) {
+ cmn_err(CE_WARN, "%s ddi_create_minor_node failed"
+ " for cpu fan", name);
+ ddi_regs_map_free(&unitp->cpufan_rhandle);
+ ddi_regs_map_free(&unitp->sysfan_rhandle);
+ ddi_soft_state_free(grfans_soft_statep, instance);
+ ddi_remove_minor_node(dip, NULL);
+
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(dip, "sys_fan", S_IFCHR,
+ DEVINST_TO_MINOR(instance) | CHANNEL_TO_MINOR(SYSTEM_FAN_CHANNEL),
+ FANS_NODE_TYPE, NULL) == DDI_FAILURE) {
+ cmn_err(CE_WARN, "%s ddi_create_minor_node failed"
+ " for system fan", name);
+ ddi_regs_map_free(&unitp->cpufan_rhandle);
+ ddi_regs_map_free(&unitp->sysfan_rhandle);
+ ddi_soft_state_free(grfans_soft_statep, instance);
+ ddi_remove_minor_node(dip, NULL);
+
+ return (DDI_FAILURE);
+ }
+
+ mutex_init(&unitp->mutex, NULL, MUTEX_DRIVER, NULL);
+
+ return (DDI_SUCCESS);
+}
+
+static int
+grfans_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ return (grfans_do_attach(dip));
+
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+static int
+grfans_do_detach(dev_info_t *dip)
+{
+ struct grfans_unit *unitp;
+ int instance;
+
+ instance = ddi_get_instance(dip);
+ unitp = ddi_get_soft_state(grfans_soft_statep, instance);
+ ddi_remove_minor_node(dip, NULL);
+
+ ddi_regs_map_free(&unitp->cpufan_rhandle);
+ ddi_regs_map_free(&unitp->sysfan_rhandle);
+
+ mutex_destroy(&unitp->mutex);
+
+ ddi_soft_state_free(grfans_soft_statep, instance);
+
+ return (DDI_SUCCESS);
+}
+
+static int
+grfans_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ return (grfans_do_detach(dip));
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/*ARGSUSED*/
+static int
+grfans_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ struct grfans_unit *unitp;
+ int err = 0;
+ int instance = MINOR_TO_DEVINST(*devp);
+ int channel;
+
+ /*
+ * must be privileged to access this device
+ */
+ if (secpolicy_sys_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ if (instance < 0) {
+ cmn_err(CE_WARN, "grfan: instance less than 0: %d\n",
+ instance);
+
+ return (ENXIO);
+ }
+
+ unitp = ddi_get_soft_state(grfans_soft_statep, instance);
+ if (unitp == NULL) {
+ cmn_err(CE_WARN, "grfan: no soft state for instance %d\n",
+ instance);
+
+ return (ENXIO);
+ }
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ channel = MINOR_TO_CHANNEL(getminor(*devp));
+
+ mutex_enter(&unitp->mutex);
+
+ if (flags & FEXCL) {
+ if (unitp->oflag[channel] != 0)
+ err = EBUSY;
+ else
+ unitp->oflag[channel] = FEXCL;
+ } else {
+ if (unitp->oflag[channel] == FEXCL)
+ err = EBUSY;
+ else
+ unitp->oflag[channel] = FOPEN;
+ }
+
+ mutex_exit(&unitp->mutex);
+
+ return (err);
+}
+
+/*ARGSUSED*/
+static int
+grfans_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ struct grfans_unit *unitp;
+ int instance = MINOR_TO_DEVINST(dev);
+ int channel;
+
+ if (instance < 0)
+ return (ENXIO);
+
+ unitp = ddi_get_soft_state(grfans_soft_statep, instance);
+ if (unitp == NULL)
+ return (ENXIO);
+
+ channel = MINOR_TO_CHANNEL(getminor(dev));
+
+ unitp->oflag[channel] = 0;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+grfans_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
+{
+ return (grfans_io(dev, uiop, B_READ));
+}
+
+/*ARGSUSED*/
+static int
+grfans_write(dev_t dev, struct uio *uiop, cred_t *cred_p)
+{
+ return (grfans_io(dev, uiop, B_WRITE));
+}
+
+static int
+grfans_io(dev_t dev, struct uio *uiop, int rw)
+{
+ struct grfans_unit *unitp;
+ int instance = MINOR_TO_DEVINST(getminor(dev));
+ int ret = 0;
+ size_t len = uiop->uio_resid;
+ int8_t out_value, req_value, reg_value;
+ caddr_t outputaddr;
+
+ if (instance < 0)
+ return (ENXIO);
+
+ if (len == 0)
+ return (0);
+
+ unitp = ddi_get_soft_state(grfans_soft_statep, instance);
+
+ if (unitp == NULL)
+ return (ENXIO);
+
+ if (MINOR_TO_CHANNEL(getminor(dev)) == CPU_FAN_CHANNEL)
+ outputaddr = &unitp->cpufan_output;
+ else
+ outputaddr = &unitp->sysfan_output;
+
+ if (rw == B_READ) {
+ if (*outputaddr == UNKNOWN_OUT)
+ return (EIO);
+ return (uiomove(outputaddr, 1, UIO_READ, uiop));
+ }
+
+ /*
+ * rw == B_WRITE.
+ */
+ if ((ret = uiomove(&req_value, sizeof (req_value), UIO_WRITE,
+ uiop)) == 0) {
+ if (MINOR_TO_CHANNEL(dev) == CPU_FAN_CHANNEL) {
+ /*
+ * Check bounds for cpu fan
+ */
+ if (req_value == 0) {
+ reg_value = CPU_FAN_0;
+ out_value = 0;
+ } else if (req_value <= 25) {
+ reg_value = CPU_FAN_25;
+ out_value = 25;
+ } else if (req_value <= 50) {
+ reg_value = CPU_FAN_50;
+ out_value = 50;
+ } else if (req_value <= 75) {
+ reg_value = CPU_FAN_75;
+ out_value = 75;
+ } else if (req_value <= 100) {
+ reg_value = CPU_FAN_100;
+ out_value = 100;
+ } else
+ ret = EINVAL;
+
+ if (ret != EINVAL) {
+ uint8_t reg;
+
+ *outputaddr = out_value;
+
+ reg = ddi_get8(unitp->cpufan_rhandle,
+ unitp->cpufan_reg);
+ reg = (reg & ~CPU_FAN_MASK) | reg_value;
+ ddi_put8(unitp->cpufan_rhandle,
+ unitp->cpufan_reg, reg);
+ (void) ddi_get8(unitp->cpufan_rhandle,
+ unitp->cpufan_reg);
+
+ if (grfans_debug) {
+ printf("set output to %d at addr %p\n",
+ out_value,
+ unitp->cpufan_reg);
+ }
+ }
+ } else {
+ if (req_value == 0) {
+ reg_value = SYS_FAN_OFF;
+ out_value = 0;
+ } else if (req_value > 0) {
+ reg_value = SYS_FAN_ON;
+ out_value = 100;
+ } else {
+ ret = EINVAL;
+ }
+
+ if (ret != EINVAL) {
+ *outputaddr = out_value;
+
+ ddi_put8(unitp->sysfan_rhandle,
+ unitp->sysfan_reg,
+ reg_value);
+ (void) ddi_get8(unitp->sysfan_rhandle,
+ unitp->sysfan_reg);
+ if (grfans_debug) {
+ printf("set SYSFAN output to %d at "
+ "addr %p\n", out_value,
+ unitp->sysfan_reg);
+ }
+ }
+ }
+ } else {
+ ret = EFAULT;
+ }
+
+ return (ret);
+}