diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/sun4u/io/gpio_87317.c | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/sun4u/io/gpio_87317.c')
| -rw-r--r-- | usr/src/uts/sun4u/io/gpio_87317.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/io/gpio_87317.c b/usr/src/uts/sun4u/io/gpio_87317.c new file mode 100644 index 0000000000..b784b83d8d --- /dev/null +++ b/usr/src/uts/sun4u/io/gpio_87317.c @@ -0,0 +1,433 @@ +/* + * 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 (c) 2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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/gpio_87317.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/file.h> +#ifdef DEBUG +#include <sys/promif.h> +#endif + + +/* a non zero value causes debug info to be displayed */ +uint_t gpio_debug_flag = 0; + + +#ifdef DEBUG +static void gpio_debug(dev_info_t *dip, char *format, uint_t arg1, uint_t arg2, + uint_t arg3, uint_t arg4, uint_t arg5); + +#define DBG(dip, format, arg1, arg2, arg3, arg4, arg5) \ + gpio_debug(dip, format, (uint_t)arg1, (uint_t)arg2, (uint_t)arg3, \ + (uint_t)arg4, (uint_t)arg5) +#else +#define DBG(dip, format, arg1, arg2, arg3, arg4, arg5) +#endif + + +/* Driver soft state structure */ +struct gpio_softc { + dev_info_t *gp_dip; + kmutex_t gp_mutex; + int gp_state; + ddi_acc_handle_t gp_handle; + uint8_t *gp_regs; +}; + +#define getsoftc(minor) \ + ((struct gpio_softc *)ddi_get_soft_state(statep, (minor))) + +/* dev_ops and cb_ops entry point function declarations */ +static int gpio_attach(dev_info_t *, ddi_attach_cmd_t); +static int gpio_detach(dev_info_t *, ddi_detach_cmd_t); +static int gpio_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int gpio_open(dev_t *, int, int, cred_t *); +static int gpio_close(dev_t, int, int, cred_t *); +static int gpio_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +struct cb_ops gpio_cb_ops = { + gpio_open, + gpio_close, + nodev, + nodev, + nodev, /* dump */ + nodev, + nodev, + gpio_ioctl, + nodev, /* devmap */ + nodev, + nodev, + nochpoll, + ddi_prop_op, + NULL, /* for STREAMS drivers */ + D_NEW | D_MP, /* driver compatibility flag */ + CB_REV, + nodev, + nodev +}; + +static struct dev_ops gpio_dev_ops = { + DEVO_REV, /* driver build version */ + 0, /* device reference count */ + gpio_getinfo, + nulldev, + nulldev, /* probe */ + gpio_attach, + gpio_detach, + nulldev, /* reset */ + &gpio_cb_ops, + (struct bus_ops *)NULL, + nulldev /* power */ +}; + +/* module configuration stuff */ +static void *statep; +extern struct mod_ops mod_driverops; +static struct modldrv modldrv = { + &mod_driverops, + "gpio driver 1.0", + &gpio_dev_ops +}; +static struct modlinkage modlinkage = { + MODREV_1, + &modldrv, + 0 +}; + + +int +_init(void) +{ + int e; + + if (e = ddi_soft_state_init(&statep, sizeof (struct gpio_softc), 1)) { + 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 +gpio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + int instance = getminor((dev_t)arg); + int retval = DDI_SUCCESS; + struct gpio_softc *softc; + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + if ((softc = getsoftc(instance)) == NULL) { + *result = (void *)NULL; + retval = DDI_FAILURE; + } else + *result = (void *)softc->gp_dip; + break; + + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)instance; + break; + + default: + retval = DDI_FAILURE; + } + + return (retval); +} + + +static int +gpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + + int instance; + struct gpio_softc *softc = NULL; + ddi_device_acc_attr_t dev_attr; + + switch (cmd) { + + case DDI_ATTACH: + + /* Allocate and get the soft state structure for this instance. */ + + instance = ddi_get_instance(dip); + DBG(dip, "attach: instance is %d", instance, 0, 0, 0, 0); + if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) + goto attach_failed; + softc = getsoftc(instance); + softc->gp_dip = dip; + softc->gp_state = 0; + mutex_init(&softc->gp_mutex, NULL, MUTEX_DRIVER, NULL); + + /* Map in the gpio device registers. */ + + dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; + dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + if (ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->gp_regs, 0, 0, + &dev_attr, &softc->gp_handle) != DDI_SUCCESS) + goto attach_failed; + DBG(dip, "attach: regs=0x%x", softc->gp_regs, 0, 0, 0, 0); + DBG(dip, "attach: port 1 data is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[0]), 0, 0, 0, 0); + DBG(dip, "attach: port 1 direction is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[1]), 0, 0, 0, 0); + DBG(dip, "attach: port 1 output type is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[2]), 0, 0, 0, 0); + DBG(dip, "attach: port 1 pull up control type is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[3]), 0, 0, 0, 0); + DBG(dip, "attach: port 2 data is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[4]), 0, 0, 0, 0); + DBG(dip, "attach: port 2 direction is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[5]), 0, 0, 0, 0); + DBG(dip, "attach: port 2 output type is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[6]), 0, 0, 0, 0); + DBG(dip, "attach: port 2 pull up control type is %x", + ddi_get8(softc->gp_handle, &softc->gp_regs[7]), 0, 0, 0, 0); + + /* Create device minor nodes. */ + + if (ddi_create_minor_node(dip, "gpio", S_IFCHR, + instance, NULL, NULL) == DDI_FAILURE) { + ddi_regs_map_free(&softc->gp_handle); + goto attach_failed; + } + + ddi_report_dev(dip); + return (DDI_SUCCESS); + + case DDI_RESUME: + + /* Nothing to do for a resume. */ + + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + +attach_failed: + if (softc) { + mutex_destroy(&softc->gp_mutex); + if (softc->gp_handle) + ddi_regs_map_free(&softc->gp_handle); + ddi_soft_state_free(statep, instance); + ddi_remove_minor_node(dip, NULL); + } + return (DDI_FAILURE); +} + + +static int +gpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int instance; + struct gpio_softc *softc; + + switch (cmd) { + case DDI_DETACH: + instance = ddi_get_instance(dip); + DBG(dip, "detach: instance is %d", instance, 0, 0, 0, 0); + if ((softc = getsoftc(instance)) == NULL) + return (ENXIO); + mutex_destroy(&softc->gp_mutex); + ddi_regs_map_free(&softc->gp_handle); + ddi_soft_state_free(statep, instance); + ddi_remove_minor_node(dip, NULL); + return (DDI_SUCCESS); + + case DDI_SUSPEND: + /* Nothing to do in the suspend case. */ + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + + +/* ARGSUSED */ +static int +gpio_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + int instance = getminor(*devp); + + DBG(NULL, "open: instance is %d", instance, 0, 0, 0, 0); + return (getsoftc(instance) == NULL ? ENXIO : 0); +} + + +/* ARGSUSED */ +static int +gpio_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + int instance = getminor(dev); + + DBG(NULL, "close: instance is %d", instance, 0, 0, 0, 0); + return (getsoftc(instance) == NULL ? ENXIO : 0); +} + + +/* ARGSUSED */ +static int +gpio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + int instance = getminor(dev); + struct gpio_softc *softc = getsoftc(instance); + gpio_87317_op_t info; + uint8_t byte; + + DBG(softc->gp_dip, "ioctl: instance is %d", instance, 0, 0, 0, 0); + + if (softc == NULL) + return (ENXIO); + + /* Copy the command from user space. */ + if (ddi_copyin((caddr_t)arg, (caddr_t)&info, sizeof (gpio_87317_op_t), + mode) != 0) + return (EFAULT); + + /* Check the command arguments. We only support port 1 in bank 0. */ + if ((info.gpio_bank != 0) || + (info.gpio_offset != GPIO_87317_PORT1_DATA)) { + return (EINVAL); + } + + /* Grap the instance's mutex to insure exclusive access. */ + mutex_enter(&softc->gp_mutex); + + /* Get the contents of the GPIO register we're suppose to modify. */ + byte = ddi_get8(softc->gp_handle, &softc->gp_regs[info.gpio_offset]); + + switch (cmd) { + case GPIO_CMD_SET_BITS: + DBG(softc->gp_dip, "ioctl: SET_BITS, byte is %x", byte, 0, 0, + 0, 0); + byte |= info.gpio_data; + ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset], + byte); + byte = ddi_get8(softc->gp_handle, + &softc->gp_regs[info.gpio_offset]); + DBG(softc->gp_dip, "ioctl: SET_BITS, byte is %x", byte, 0, 0, + 0, 0); + break; + + case GPIO_CMD_CLR_BITS: + DBG(softc->gp_dip, "ioctl: CLR_BITS, byte is %x", byte, 0, 0, + 0, 0); + byte &= ~info.gpio_data; + ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset], + byte); + byte = ddi_get8(softc->gp_handle, + &softc->gp_regs[info.gpio_offset]); + DBG(softc->gp_dip, "ioctl: CLR_BITS, byte is %x", byte, 0, 0, + 0, 0); + break; + + case GPIO_CMD_GET: + DBG(softc->gp_dip, "ioctl: GPIO_CMD_GET", 0, 0, 0, 0, 0); + info.gpio_data = byte; + if (ddi_copyout((caddr_t)&info, (caddr_t)arg, + sizeof (gpio_87317_op_t), mode) != 0) { + mutex_exit(&softc->gp_mutex); + return (EFAULT); + } + break; + + case GPIO_CMD_SET: + DBG(softc->gp_dip, "ioctl: GPIO_CMD_SET", 0, 0, 0, 0, 0); + ddi_put8(softc->gp_handle, &softc->gp_regs[info.gpio_offset], + info.gpio_data); + break; + + default: + mutex_exit(&softc->gp_mutex); + return (EINVAL); + } + + mutex_exit(&softc->gp_mutex); + return (0); +} + + +#ifdef DEBUG +void +gpio_debug(dev_info_t *dip, char *format, uint_t arg1, uint_t arg2, uint_t arg3, + uint_t arg4, uint_t arg5) +{ + if (gpio_debug_flag == 0) { + return; + } + + if (dip == NULL) { + prom_printf("gpio: "); + } else { + prom_printf("%s%d: ", ddi_driver_name(dip), + ddi_get_instance(dip)); + } + prom_printf(format, arg1, arg2, arg3, arg4, arg5); + prom_printf("\n"); +} +#endif |
