125cf1a30Sjl /*
225cf1a30Sjl * CDDL HEADER START
325cf1a30Sjl *
425cf1a30Sjl * The contents of this file are subject to the terms of the
525cf1a30Sjl * Common Development and Distribution License (the "License").
625cf1a30Sjl * You may not use this file except in compliance with the License.
725cf1a30Sjl *
825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl * See the License for the specific language governing permissions
1125cf1a30Sjl * and limitations under the License.
1225cf1a30Sjl *
1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl *
1925cf1a30Sjl * CDDL HEADER END
2025cf1a30Sjl */
2119397407SSherry Moore
2225cf1a30Sjl /*
2325cf1a30Sjl * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
2425cf1a30Sjl */
2525cf1a30Sjl
2619397407SSherry Moore /*
27*a1bf6e2eSChristopher Baumbauer - Oracle America - San Diego United States * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2819397407SSherry Moore */
2919397407SSherry Moore
3025cf1a30Sjl
3125cf1a30Sjl #include <sys/types.h>
3225cf1a30Sjl #include <sys/time.h>
3325cf1a30Sjl #include <sys/errno.h>
3425cf1a30Sjl #include <sys/cmn_err.h>
3525cf1a30Sjl #include <sys/param.h>
3625cf1a30Sjl #include <sys/modctl.h>
3725cf1a30Sjl #include <sys/conf.h>
3825cf1a30Sjl #include <sys/open.h>
3925cf1a30Sjl #include <sys/stat.h>
4025cf1a30Sjl #include <sys/ddi.h>
4125cf1a30Sjl #include <sys/sunddi.h>
4225cf1a30Sjl #include <sys/file.h>
4325cf1a30Sjl #include <sys/intr.h>
4425cf1a30Sjl #include <sys/machsystm.h>
4525cf1a30Sjl
4625cf1a30Sjl #define PNLIE_MASK 0x010 /* interrupt enable/disable */
4725cf1a30Sjl #define PNLINT_MASK 0x001 /* interrupted flag */
4825cf1a30Sjl
4925cf1a30Sjl #ifdef DEBUG
5025cf1a30Sjl int panel_debug = 0;
5125cf1a30Sjl static void panel_ddi_put8(ddi_acc_handle_t, uint8_t *, uint8_t);
5225cf1a30Sjl #define DCMN_ERR(x) if (panel_debug) cmn_err x
5325cf1a30Sjl
5425cf1a30Sjl #else
5525cf1a30Sjl
5625cf1a30Sjl #define DCMN_ERR(x)
5725cf1a30Sjl #define panel_ddi_put8(x, y, z) ddi_put8(x, y, z)
5825cf1a30Sjl
5925cf1a30Sjl #endif
6025cf1a30Sjl
6125cf1a30Sjl static int panel_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
6225cf1a30Sjl static int panel_attach(dev_info_t *, ddi_attach_cmd_t);
6325cf1a30Sjl static int panel_detach(dev_info_t *, ddi_detach_cmd_t);
6425cf1a30Sjl static uint_t panel_intr(caddr_t);
6525cf1a30Sjl static int panel_open(dev_t *, int, int, cred_t *);
6625cf1a30Sjl static int panel_close(dev_t, int, int, cred_t *);
6725cf1a30Sjl
6825cf1a30Sjl static char *panel_name = "oplpanel";
6925cf1a30Sjl int panel_enable = 1; /* enable or disable */
7025cf1a30Sjl
71b0fc0e77Sgovinda extern uint64_t cpc_level15_inum; /* in cpc_subr.c */
7225cf1a30Sjl
7325cf1a30Sjl struct panel_state {
7425cf1a30Sjl dev_info_t *dip;
7525cf1a30Sjl ddi_iblock_cookie_t iblock_cookie;
7625cf1a30Sjl ddi_acc_handle_t panel_regs_handle;
7725cf1a30Sjl uint8_t *panelregs; /* mapping address */
7825cf1a30Sjl uint8_t panelregs_state; /* keeping regs. */
7925cf1a30Sjl };
8025cf1a30Sjl
8125cf1a30Sjl struct cb_ops panel_cb_ops = {
8225cf1a30Sjl nodev, /* open */
8325cf1a30Sjl nodev, /* close */
8425cf1a30Sjl nodev, /* strategy */
8525cf1a30Sjl nodev, /* print */
8625cf1a30Sjl nodev, /* dump */
8725cf1a30Sjl nodev, /* read */
8825cf1a30Sjl nodev, /* write */
8925cf1a30Sjl nodev, /* ioctl */
9025cf1a30Sjl nodev, /* devmap */
9125cf1a30Sjl nodev, /* mmap */
9225cf1a30Sjl nodev, /* segmap */
9325cf1a30Sjl nochpoll, /* poll */
9425cf1a30Sjl nodev, /* prop_op */
9525cf1a30Sjl NULL, /* streamtab */
9625cf1a30Sjl D_NEW | D_MP | D_HOTPLUG, /* flag */
9725cf1a30Sjl CB_REV, /* cb_rev */
9825cf1a30Sjl nodev, /* async I/O read entry point */
9925cf1a30Sjl nodev /* async I/O write entry point */
10025cf1a30Sjl };
10125cf1a30Sjl
10225cf1a30Sjl static struct dev_ops panel_dev_ops = {
10325cf1a30Sjl DEVO_REV, /* driver build version */
10425cf1a30Sjl 0, /* device reference count */
10525cf1a30Sjl panel_getinfo, /* getinfo */
10625cf1a30Sjl nulldev, /* identify */
10725cf1a30Sjl nulldev, /* probe */
10825cf1a30Sjl panel_attach, /* attach */
10925cf1a30Sjl panel_detach, /* detach */
11025cf1a30Sjl nulldev, /* reset */
11125cf1a30Sjl &panel_cb_ops, /* cb_ops */
11225cf1a30Sjl NULL, /* bus_ops */
11319397407SSherry Moore nulldev, /* power */
11419397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */
11525cf1a30Sjl };
11625cf1a30Sjl
11725cf1a30Sjl /* module configuration stuff */
11825cf1a30Sjl static void *panelstates;
11925cf1a30Sjl extern struct mod_ops mod_driverops;
12025cf1a30Sjl
12125cf1a30Sjl static struct modldrv modldrv = {
12225cf1a30Sjl &mod_driverops,
12319397407SSherry Moore "OPL panel driver",
12425cf1a30Sjl &panel_dev_ops
12525cf1a30Sjl };
12625cf1a30Sjl
12725cf1a30Sjl static struct modlinkage modlinkage = {
12825cf1a30Sjl MODREV_1,
12925cf1a30Sjl &modldrv,
13025cf1a30Sjl 0
13125cf1a30Sjl };
13225cf1a30Sjl
13325cf1a30Sjl
13425cf1a30Sjl int
_init(void)13525cf1a30Sjl _init(void)
13625cf1a30Sjl {
13725cf1a30Sjl int status;
13825cf1a30Sjl
13925cf1a30Sjl DCMN_ERR((CE_CONT, "%s: _init\n", panel_name));
14025cf1a30Sjl
14125cf1a30Sjl status = ddi_soft_state_init(&panelstates,
14225cf1a30Sjl sizeof (struct panel_state), 0);
14325cf1a30Sjl if (status != 0) {
14425cf1a30Sjl cmn_err(CE_WARN, "%s: ddi_soft_state_init failed.",
14525cf1a30Sjl panel_name);
14625cf1a30Sjl return (status);
14725cf1a30Sjl }
14825cf1a30Sjl
14925cf1a30Sjl status = mod_install(&modlinkage);
15025cf1a30Sjl if (status != 0) {
15125cf1a30Sjl ddi_soft_state_fini(&panelstates);
15225cf1a30Sjl }
15325cf1a30Sjl
15425cf1a30Sjl return (status);
15525cf1a30Sjl }
15625cf1a30Sjl
15725cf1a30Sjl int
_fini(void)15825cf1a30Sjl _fini(void)
15925cf1a30Sjl {
16025cf1a30Sjl /*
16125cf1a30Sjl * Can't unload to make sure the panel switch always works.
16225cf1a30Sjl */
16325cf1a30Sjl return (EBUSY);
16425cf1a30Sjl }
16525cf1a30Sjl
16625cf1a30Sjl int
_info(struct modinfo * modinfop)16725cf1a30Sjl _info(struct modinfo *modinfop)
16825cf1a30Sjl {
16925cf1a30Sjl return (mod_info(&modlinkage, modinfop));
17025cf1a30Sjl }
17125cf1a30Sjl
17225cf1a30Sjl static int
panel_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)17325cf1a30Sjl panel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
17425cf1a30Sjl {
17525cf1a30Sjl
17625cf1a30Sjl int instance;
17725cf1a30Sjl struct panel_state *statep = NULL;
17825cf1a30Sjl
17925cf1a30Sjl ddi_device_acc_attr_t access_attr = {
18025cf1a30Sjl DDI_DEVICE_ATTR_V0,
18125cf1a30Sjl DDI_STRUCTURE_BE_ACC,
18225cf1a30Sjl DDI_STRICTORDER_ACC
18325cf1a30Sjl };
18425cf1a30Sjl
18525cf1a30Sjl instance = ddi_get_instance(dip);
18625cf1a30Sjl
18725cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: attach\n", panel_name, instance));
18825cf1a30Sjl
18925cf1a30Sjl switch (cmd) {
19025cf1a30Sjl case DDI_ATTACH:
19125cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: DDI_ATTACH\n",
19225cf1a30Sjl panel_name, instance));
19325cf1a30Sjl break;
19425cf1a30Sjl
19525cf1a30Sjl case DDI_RESUME:
19625cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: DDI_RESUME\n",
19725cf1a30Sjl panel_name, instance));
19825cf1a30Sjl
19925cf1a30Sjl if ((statep = (struct panel_state *)
20025cf1a30Sjl ddi_get_soft_state(panelstates, instance)) == NULL) {
20125cf1a30Sjl cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
20225cf1a30Sjl panel_name, instance);
20325cf1a30Sjl return (DDI_FAILURE);
20425cf1a30Sjl }
20525cf1a30Sjl
20625cf1a30Sjl /* enable the interrupt just in case */
20725cf1a30Sjl panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
20825cf1a30Sjl statep->panelregs_state);
20925cf1a30Sjl return (DDI_SUCCESS);
21025cf1a30Sjl
21125cf1a30Sjl default:
21225cf1a30Sjl return (DDI_FAILURE);
21325cf1a30Sjl }
21425cf1a30Sjl
21525cf1a30Sjl /*
21625cf1a30Sjl * Attach routine
21725cf1a30Sjl */
21825cf1a30Sjl
21925cf1a30Sjl /* alloc and get soft state */
22025cf1a30Sjl if (ddi_soft_state_zalloc(panelstates, instance) != DDI_SUCCESS) {
22125cf1a30Sjl cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc failed.",
22225cf1a30Sjl panel_name, instance);
22325cf1a30Sjl goto attach_failed2;
22425cf1a30Sjl }
22525cf1a30Sjl if ((statep = (struct panel_state *)
22625cf1a30Sjl ddi_get_soft_state(panelstates, instance)) == NULL) {
22725cf1a30Sjl cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
22825cf1a30Sjl panel_name, instance);
22925cf1a30Sjl goto attach_failed1;
23025cf1a30Sjl }
23125cf1a30Sjl
23225cf1a30Sjl /* set the dip in the soft state */
23325cf1a30Sjl statep->dip = dip;
23425cf1a30Sjl
23525cf1a30Sjl /* mapping register */
23625cf1a30Sjl if (ddi_regs_map_setup(dip, 0, (caddr_t *)&statep->panelregs,
23725cf1a30Sjl 0, 0, /* the entire space is mapped */
23825cf1a30Sjl &access_attr, &statep->panel_regs_handle) != DDI_SUCCESS) {
23925cf1a30Sjl cmn_err(CE_WARN, "%s%d: ddi_regs_map_setup failed.",
24025cf1a30Sjl panel_name, instance);
24125cf1a30Sjl goto attach_failed1;
24225cf1a30Sjl }
24325cf1a30Sjl
24425cf1a30Sjl /* setup the interrupt handler */
24507d06da5SSurya Prakki (void) ddi_get_iblock_cookie(dip, 0, &statep->iblock_cookie);
24625cf1a30Sjl if (ddi_add_intr(dip, 0, &statep->iblock_cookie, 0, &panel_intr,
24725cf1a30Sjl (caddr_t)statep) != DDI_SUCCESS) {
24825cf1a30Sjl cmn_err(CE_WARN, "%s%d: cannot add interrupt handler.",
24925cf1a30Sjl panel_name, instance);
25025cf1a30Sjl goto attach_failed0;
25125cf1a30Sjl }
25225cf1a30Sjl
25325cf1a30Sjl /* ATTACH SUCCESS */
25425cf1a30Sjl
25525cf1a30Sjl /* announce the device */
25625cf1a30Sjl ddi_report_dev(dip);
25725cf1a30Sjl
25825cf1a30Sjl /* turn on interrupt */
25925cf1a30Sjl statep->panelregs_state = 0 | PNLIE_MASK;
26025cf1a30Sjl panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
26125cf1a30Sjl statep->panelregs_state);
26225cf1a30Sjl
26325cf1a30Sjl return (DDI_SUCCESS);
26425cf1a30Sjl
26525cf1a30Sjl attach_failed0:
26625cf1a30Sjl ddi_regs_map_free(&statep->panel_regs_handle);
26725cf1a30Sjl attach_failed1:
26825cf1a30Sjl ddi_soft_state_free(panelstates, instance);
26925cf1a30Sjl attach_failed2:
27025cf1a30Sjl DCMN_ERR((CE_NOTE, "%s%d: attach failed", panel_name, instance));
27125cf1a30Sjl return (DDI_FAILURE);
27225cf1a30Sjl }
27325cf1a30Sjl
27425cf1a30Sjl static int
panel_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)27525cf1a30Sjl panel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
27625cf1a30Sjl {
27725cf1a30Sjl int instance;
27825cf1a30Sjl struct panel_state *statep;
27925cf1a30Sjl
28025cf1a30Sjl instance = ddi_get_instance(dip);
28125cf1a30Sjl
28225cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: detach\n", panel_name, instance));
28325cf1a30Sjl
28425cf1a30Sjl if ((statep = (struct panel_state *)
28525cf1a30Sjl ddi_get_soft_state(panelstates, instance)) == NULL) {
28625cf1a30Sjl cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
28725cf1a30Sjl panel_name, instance);
28825cf1a30Sjl return (DDI_FAILURE);
28925cf1a30Sjl }
29025cf1a30Sjl
29125cf1a30Sjl switch (cmd) {
29225cf1a30Sjl case DDI_DETACH:
29325cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: DDI_DETACH\n",
29425cf1a30Sjl panel_name, instance));
29525cf1a30Sjl
29625cf1a30Sjl /* turn off interrupt */
29725cf1a30Sjl statep->panelregs_state &= ~PNLIE_MASK;
29825cf1a30Sjl panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
29925cf1a30Sjl statep->panelregs_state);
30025cf1a30Sjl
30125cf1a30Sjl /* free all resources for the dip */
30225cf1a30Sjl ddi_remove_intr(dip, 0, statep->iblock_cookie);
30325cf1a30Sjl
30425cf1a30Sjl /* need not free iblock_cookie */
30525cf1a30Sjl ddi_regs_map_free(&statep->panel_regs_handle);
30625cf1a30Sjl ddi_soft_state_free(panelstates, instance);
30725cf1a30Sjl
30825cf1a30Sjl return (DDI_SUCCESS);
30925cf1a30Sjl
31025cf1a30Sjl case DDI_SUSPEND:
31125cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: DDI_SUSPEND\n",
31225cf1a30Sjl panel_name, instance));
31325cf1a30Sjl return (DDI_SUCCESS);
31425cf1a30Sjl
31525cf1a30Sjl default:
31625cf1a30Sjl return (DDI_FAILURE);
31725cf1a30Sjl
31825cf1a30Sjl }
31925cf1a30Sjl /* Not reached */
32025cf1a30Sjl }
32125cf1a30Sjl
32225cf1a30Sjl /*ARGSUSED*/
32325cf1a30Sjl static int
panel_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)32425cf1a30Sjl panel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
32525cf1a30Sjl {
32625cf1a30Sjl struct panel_state *statep;
32725cf1a30Sjl int instance;
32825cf1a30Sjl dev_t dev = (dev_t)arg;
32925cf1a30Sjl
33025cf1a30Sjl instance = getminor(dev);
33125cf1a30Sjl
33225cf1a30Sjl DCMN_ERR((CE_CONT, "%s%d: getinfo\n", panel_name, instance));
33325cf1a30Sjl
33425cf1a30Sjl switch (cmd) {
33525cf1a30Sjl case DDI_INFO_DEVT2DEVINFO:
33625cf1a30Sjl if ((statep = (struct panel_state *)
33725cf1a30Sjl ddi_get_soft_state(panelstates, instance)) == NULL) {
33825cf1a30Sjl cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
33925cf1a30Sjl panel_name, instance);
34025cf1a30Sjl *resultp = NULL;
34125cf1a30Sjl return (DDI_FAILURE);
34225cf1a30Sjl }
34325cf1a30Sjl *resultp = statep->dip;
34425cf1a30Sjl break;
34525cf1a30Sjl case DDI_INFO_DEVT2INSTANCE:
34625cf1a30Sjl *resultp = (void *)(uintptr_t)instance;
34725cf1a30Sjl break;
34825cf1a30Sjl default:
34925cf1a30Sjl return (DDI_FAILURE);
35025cf1a30Sjl }
35125cf1a30Sjl
35225cf1a30Sjl return (DDI_SUCCESS);
35325cf1a30Sjl }
35425cf1a30Sjl
35525cf1a30Sjl static uint_t
panel_intr(caddr_t arg)35625cf1a30Sjl panel_intr(caddr_t arg)
35725cf1a30Sjl {
35825cf1a30Sjl struct panel_state *statep = (struct panel_state *)arg;
35925cf1a30Sjl
36025cf1a30Sjl /* to confirm the validity of the interrupt */
36125cf1a30Sjl if (!(ddi_get8(statep->panel_regs_handle, statep->panelregs) &
36225cf1a30Sjl PNLINT_MASK)) {
36325cf1a30Sjl return (DDI_INTR_UNCLAIMED);
36425cf1a30Sjl }
36525cf1a30Sjl
36697cc145dShyw /*
36797cc145dShyw * Clear the PNLINT bit
36897cc145dShyw * HW reported that there might be a delay in the PNLINT bit
36997cc145dShyw * clearing. We force synchronization by attempting to read
37097cc145dShyw * back the reg after clearing the bit.
37197cc145dShyw */
37225cf1a30Sjl panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
37325cf1a30Sjl statep->panelregs_state | PNLINT_MASK);
37407d06da5SSurya Prakki (void) ddi_get8(statep->panel_regs_handle, statep->panelregs);
37525cf1a30Sjl
37625cf1a30Sjl if (panel_enable) {
37725cf1a30Sjl /* avoid double panic */
37825cf1a30Sjl panel_enable = 0;
37925cf1a30Sjl
38025cf1a30Sjl cmn_err(CE_PANIC,
38125cf1a30Sjl "System Panel Driver: Emergency panic request "
38225cf1a30Sjl "detected!");
38325cf1a30Sjl /* Not reached */
38425cf1a30Sjl }
38525cf1a30Sjl
38625cf1a30Sjl return (DDI_INTR_CLAIMED);
38725cf1a30Sjl }
38825cf1a30Sjl
38925cf1a30Sjl #ifdef DEBUG
39025cf1a30Sjl static void
panel_ddi_put8(ddi_acc_handle_t handle,uint8_t * dev_addr,uint8_t value)39125cf1a30Sjl panel_ddi_put8(ddi_acc_handle_t handle, uint8_t *dev_addr, uint8_t value)
39225cf1a30Sjl {
39325cf1a30Sjl if (panel_debug) {
39425cf1a30Sjl cmn_err(CE_CONT, "%s: old value = 0x%x\n",
39525cf1a30Sjl panel_name, ddi_get8(handle, dev_addr));
39625cf1a30Sjl cmn_err(CE_CONT, "%s: writing value = 0x%x\n",
39725cf1a30Sjl panel_name, value);
39825cf1a30Sjl ddi_put8(handle, dev_addr, value);
39925cf1a30Sjl cmn_err(CE_CONT, "%s: new value = 0x%x\n",
40025cf1a30Sjl panel_name, ddi_get8(handle, dev_addr));
40125cf1a30Sjl } else {
40225cf1a30Sjl ddi_put8(handle, dev_addr, value);
40325cf1a30Sjl }
40425cf1a30Sjl }
40525cf1a30Sjl #endif
406