103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
519397407SSherry Moore * Common Development and Distribution License (the "License").
619397407SSherry Moore * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel /*
2207d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2303831d35Sstevel * Use is subject to license terms.
24*89b43686SBayard Bell * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel /*
2803831d35Sstevel * I2C leaf driver for the PCF8591
2903831d35Sstevel */
3003831d35Sstevel
3103831d35Sstevel #include <sys/param.h>
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/signal.h>
3403831d35Sstevel #include <sys/errno.h>
3503831d35Sstevel #include <sys/file.h>
3603831d35Sstevel #include <sys/termio.h>
3703831d35Sstevel #include <sys/termios.h>
3803831d35Sstevel #include <sys/cmn_err.h>
3903831d35Sstevel #include <sys/stream.h>
4003831d35Sstevel #include <sys/strsun.h>
4103831d35Sstevel #include <sys/stropts.h>
4203831d35Sstevel #include <sys/strtty.h>
4303831d35Sstevel #include <sys/debug.h>
4403831d35Sstevel #include <sys/eucioctl.h>
4503831d35Sstevel #include <sys/cred.h>
4603831d35Sstevel #include <sys/uio.h>
4703831d35Sstevel #include <sys/stat.h>
4803831d35Sstevel #include <sys/kmem.h>
4903831d35Sstevel
5003831d35Sstevel #include <sys/ddi.h>
5103831d35Sstevel #include <sys/sunddi.h>
5203831d35Sstevel #include <sys/obpdefs.h>
5303831d35Sstevel #include <sys/conf.h>
5403831d35Sstevel #include <sys/modctl.h>
5503831d35Sstevel #include <sys/stat.h>
5603831d35Sstevel #include <sys/open.h>
5703831d35Sstevel #include <sys/uio.h>
5803831d35Sstevel
5903831d35Sstevel #include <sys/i2c/misc/i2c_svc.h>
6003831d35Sstevel #include <sys/envctrl_gen.h>
6103831d35Sstevel #include <sys/netract_gen.h>
6203831d35Sstevel #include <sys/pcf8591_nct.h>
6303831d35Sstevel
6403831d35Sstevel
6503831d35Sstevel /*
6603831d35Sstevel * CONTROL OF CHIP
6703831d35Sstevel * PCF8591 Temp sensing control register definitions
6803831d35Sstevel *
6903831d35Sstevel * ---------------------------------------------
7003831d35Sstevel * | 0 | AOE | X | X | 0 | AIF | X | X |
7103831d35Sstevel * ---------------------------------------------
7203831d35Sstevel * AOE = Analog out enable.. not used on out implementation
7303831d35Sstevel * 5 & 4 = Analog Input Programming.. see data sheet for bits..
7403831d35Sstevel *
7503831d35Sstevel * AIF = Auto increment flag
7603831d35Sstevel * bits 1 & 0 are for the Chennel number.
7703831d35Sstevel */
7803831d35Sstevel
7903831d35Sstevel
8003831d35Sstevel #define I2CTRANS_DATA 0
8103831d35Sstevel #define I2CRAW_DATA 1
8203831d35Sstevel #define TEMP_TABLE_SIZE 256
8303831d35Sstevel
8403831d35Sstevel #define SHUTDOWN_TEMP_MIN 55
8503831d35Sstevel #define SHUTDOWN_TEMP_MAX 85
8603831d35Sstevel
8703831d35Sstevel #ifdef DEBUG
8803831d35Sstevel #define dbg_print(level, str) cmn_err(level, str);
8903831d35Sstevel #else
9003831d35Sstevel #define dbg_print(level, str) {; }
9103831d35Sstevel #endif
9203831d35Sstevel
9303831d35Sstevel
9403831d35Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
9503831d35Sstevel static uchar_t _cpu_temps[TEMP_TABLE_SIZE + 4]; /* see attach */
9603831d35Sstevel
9703831d35Sstevel static void *pcf8591_soft_statep;
9803831d35Sstevel
9903831d35Sstevel /*
10003831d35Sstevel * cb ops (only need ioctl)
10103831d35Sstevel */
10203831d35Sstevel static int pcf8591_open(dev_t *, int, int, cred_t *);
10303831d35Sstevel static int pcf8591_close(dev_t, int, int, cred_t *);
10403831d35Sstevel static int pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
10503831d35Sstevel static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
10603831d35Sstevel
10703831d35Sstevel static struct cb_ops pcf8591_cbops = {
10803831d35Sstevel pcf8591_open, /* open */
10903831d35Sstevel pcf8591_close, /* close */
11003831d35Sstevel nodev, /* strategy */
11103831d35Sstevel nodev, /* print */
11203831d35Sstevel nodev, /* dump */
11303831d35Sstevel pcf8591_read, /* read */
11403831d35Sstevel nodev, /* write */
11503831d35Sstevel pcf8591_ioctl, /* ioctl */
11603831d35Sstevel nodev, /* devmap */
11703831d35Sstevel nodev, /* mmap */
11803831d35Sstevel nodev, /* segmap */
11903831d35Sstevel nochpoll, /* poll */
12003831d35Sstevel ddi_prop_op, /* cb_prop_op */
12103831d35Sstevel NULL, /* streamtab */
12203831d35Sstevel D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
12303831d35Sstevel CB_REV, /* rev */
12403831d35Sstevel nodev, /* int (*cb_aread)() */
12503831d35Sstevel nodev /* int (*cb_awrite)() */
12603831d35Sstevel };
12703831d35Sstevel
12803831d35Sstevel /*
12903831d35Sstevel * dev ops
13003831d35Sstevel */
13103831d35Sstevel static int pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
13203831d35Sstevel void **result);
13303831d35Sstevel static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
13403831d35Sstevel static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
13503831d35Sstevel
13603831d35Sstevel /* kstat routines */
13703831d35Sstevel static int pcf8591_add_kstats(struct pcf8591_unit *);
13803831d35Sstevel static void pcf8591_delete_kstats(struct pcf8591_unit *);
13903831d35Sstevel static int pcf8591_temp_kstat_update(kstat_t *, int);
14003831d35Sstevel static int pcf8591_read_chip(struct pcf8591_unit *, uint8_t, int);
14103831d35Sstevel static int pcf8591_read_props(struct pcf8591_unit *unitp);
14203831d35Sstevel
14303831d35Sstevel static struct dev_ops pcf8591_ops = {
14403831d35Sstevel DEVO_REV,
14503831d35Sstevel 0,
14603831d35Sstevel pcf8591_info,
14703831d35Sstevel nulldev,
14803831d35Sstevel nulldev,
14903831d35Sstevel pcf8591_attach,
15003831d35Sstevel pcf8591_detach,
15103831d35Sstevel nodev,
15203831d35Sstevel &pcf8591_cbops,
15319397407SSherry Moore NULL,
15419397407SSherry Moore NULL,
15519397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */
15603831d35Sstevel };
15703831d35Sstevel
15803831d35Sstevel extern struct mod_ops mod_driverops;
15903831d35Sstevel
16003831d35Sstevel static struct modldrv pcf8591_modldrv = {
16103831d35Sstevel &mod_driverops, /* type of module - driver */
16219397407SSherry Moore "Netract pcf8591 (adio)",
16303831d35Sstevel &pcf8591_ops,
16403831d35Sstevel };
16503831d35Sstevel
16603831d35Sstevel static struct modlinkage pcf8591_modlinkage = {
16703831d35Sstevel MODREV_1,
16803831d35Sstevel &pcf8591_modldrv,
16903831d35Sstevel 0
17003831d35Sstevel };
17103831d35Sstevel
17203831d35Sstevel int pcf8591_debug = 0x02;
17303831d35Sstevel static uint8_t translate_cputemp(uint8_t value);
17403831d35Sstevel
17503831d35Sstevel int
_init(void)17603831d35Sstevel _init(void)
17703831d35Sstevel {
17803831d35Sstevel register int error;
17903831d35Sstevel
18003831d35Sstevel error = mod_install(&pcf8591_modlinkage);
18103831d35Sstevel if (error == 0) {
18203831d35Sstevel (void) ddi_soft_state_init(&pcf8591_soft_statep,
18303831d35Sstevel sizeof (struct pcf8591_unit), PCF8591_MAX_DEVS);
18403831d35Sstevel }
18503831d35Sstevel
18603831d35Sstevel return (error);
18703831d35Sstevel }
18803831d35Sstevel
18903831d35Sstevel int
_fini(void)19003831d35Sstevel _fini(void)
19103831d35Sstevel {
19203831d35Sstevel register int error;
19303831d35Sstevel
19403831d35Sstevel error = mod_remove(&pcf8591_modlinkage);
19503831d35Sstevel if (error == 0) {
19603831d35Sstevel ddi_soft_state_fini(&pcf8591_soft_statep);
19703831d35Sstevel }
19803831d35Sstevel
19903831d35Sstevel return (error);
20003831d35Sstevel }
20103831d35Sstevel
20203831d35Sstevel int
_info(struct modinfo * modinfop)20303831d35Sstevel _info(struct modinfo *modinfop)
20403831d35Sstevel {
20503831d35Sstevel return (mod_info(&pcf8591_modlinkage, modinfop));
20603831d35Sstevel }
20703831d35Sstevel
20803831d35Sstevel /*ARGSUSED*/
20903831d35Sstevel static int
pcf8591_open(dev_t * devp,int flags,int otyp,cred_t * credp)21003831d35Sstevel pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp)
21103831d35Sstevel {
21203831d35Sstevel int err = 0;
21303831d35Sstevel struct pcf8591_unit *unitp;
21403831d35Sstevel minor_t minor = getminor(*devp);
21503831d35Sstevel
21603831d35Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
21703831d35Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
21803831d35Sstevel
21903831d35Sstevel if (instance < 0) {
22003831d35Sstevel return (ENXIO);
22103831d35Sstevel }
22203831d35Sstevel
22303831d35Sstevel unitp = (struct pcf8591_unit *)
22403831d35Sstevel ddi_get_soft_state(pcf8591_soft_statep, instance);
22503831d35Sstevel
22603831d35Sstevel if (unitp == NULL) {
22703831d35Sstevel return (ENXIO);
22803831d35Sstevel }
22903831d35Sstevel
23003831d35Sstevel if (otyp != OTYP_CHR) {
23103831d35Sstevel return (EINVAL);
23203831d35Sstevel }
23303831d35Sstevel
23403831d35Sstevel mutex_enter(&unitp->umutex);
23503831d35Sstevel
23603831d35Sstevel if (flags & FEXCL) {
23703831d35Sstevel if (unitp->pcf8591_oflag[channel] != 0) {
23803831d35Sstevel err = EBUSY;
23903831d35Sstevel } else {
24003831d35Sstevel unitp->pcf8591_oflag[channel] = FEXCL;
24103831d35Sstevel }
24203831d35Sstevel } else {
24303831d35Sstevel if (unitp->pcf8591_oflag[channel] == FEXCL) {
24403831d35Sstevel err = EBUSY;
24503831d35Sstevel } else {
24603831d35Sstevel unitp->pcf8591_oflag[channel] = FOPEN;
24703831d35Sstevel }
24803831d35Sstevel }
24903831d35Sstevel
25003831d35Sstevel mutex_exit(&unitp->umutex);
25103831d35Sstevel
25203831d35Sstevel return (err);
25303831d35Sstevel }
25403831d35Sstevel
25503831d35Sstevel /*ARGSUSED*/
25603831d35Sstevel static int
pcf8591_close(dev_t devp,int flags,int otyp,cred_t * credp)25703831d35Sstevel pcf8591_close(dev_t devp, int flags, int otyp, cred_t *credp)
25803831d35Sstevel {
25903831d35Sstevel struct pcf8591_unit *unitp;
26003831d35Sstevel minor_t minor = getminor(devp);
26103831d35Sstevel
26203831d35Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
26303831d35Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
26403831d35Sstevel
26503831d35Sstevel #ifdef lint
26603831d35Sstevel flags = flags;
26703831d35Sstevel otyp = otyp;
26803831d35Sstevel #endif
26903831d35Sstevel
27003831d35Sstevel if (instance < 0) {
27103831d35Sstevel return (ENXIO);
27203831d35Sstevel }
27303831d35Sstevel
27403831d35Sstevel unitp = (struct pcf8591_unit *)
27503831d35Sstevel ddi_get_soft_state(pcf8591_soft_statep, instance);
27603831d35Sstevel
27703831d35Sstevel if (unitp == NULL) {
27803831d35Sstevel return (ENXIO);
27903831d35Sstevel }
28003831d35Sstevel
28103831d35Sstevel mutex_enter(&unitp->umutex);
28203831d35Sstevel
28303831d35Sstevel unitp->pcf8591_oflag[channel] = 0;
28403831d35Sstevel
28503831d35Sstevel mutex_exit(&unitp->umutex);
28603831d35Sstevel
28703831d35Sstevel return (DDI_SUCCESS);
28803831d35Sstevel }
28903831d35Sstevel
29003831d35Sstevel static int
pcf8591_io(dev_t dev,struct uio * uiop,int rw)29103831d35Sstevel pcf8591_io(dev_t dev, struct uio *uiop, int rw)
29203831d35Sstevel {
29303831d35Sstevel int err = 0;
29403831d35Sstevel struct pcf8591_unit *unitp;
29503831d35Sstevel minor_t minor = getminor(dev);
29603831d35Sstevel
29703831d35Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
29803831d35Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
29903831d35Sstevel
30003831d35Sstevel int bytes_to_rw;
30103831d35Sstevel int translate = 0;
30203831d35Sstevel
30303831d35Sstevel /*
30403831d35Sstevel * At this point we don't have a write operation to pcf8591.
30503831d35Sstevel */
30603831d35Sstevel if (rw == B_WRITE) {
30703831d35Sstevel return (EACCES);
30803831d35Sstevel }
30903831d35Sstevel
31003831d35Sstevel if (instance < 0) {
31103831d35Sstevel return (ENXIO);
31203831d35Sstevel }
31303831d35Sstevel
31403831d35Sstevel unitp = (struct pcf8591_unit *)
31503831d35Sstevel ddi_get_soft_state(pcf8591_soft_statep, instance);
31603831d35Sstevel if (unitp == NULL) {
31703831d35Sstevel return (ENXIO);
31803831d35Sstevel }
31903831d35Sstevel
32003831d35Sstevel if ((bytes_to_rw = uiop->uio_resid) > PCF8591_TRAN_SIZE) {
32103831d35Sstevel return (EINVAL);
32203831d35Sstevel }
32303831d35Sstevel
32403831d35Sstevel /*
32503831d35Sstevel * Need to serialize all read operations, since there is a single
32603831d35Sstevel * i2c_transfer_t structure allocated for all read and write ops.
32703831d35Sstevel * We can't share the i2c bus among multiple transactions anyway,
32803831d35Sstevel * so this does not affect performance.
32903831d35Sstevel */
33003831d35Sstevel mutex_enter(&unitp->umutex);
33103831d35Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
33203831d35Sstevel if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
33303831d35Sstevel mutex_exit(&unitp->umutex);
33403831d35Sstevel
33503831d35Sstevel return (EINTR);
33603831d35Sstevel }
33703831d35Sstevel }
33803831d35Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
33903831d35Sstevel mutex_exit(&unitp->umutex);
34003831d35Sstevel
34103831d35Sstevel if (bytes_to_rw == 1)
34203831d35Sstevel translate = 1;
34303831d35Sstevel /*
34403831d35Sstevel * Event sequence:
34503831d35Sstevel * 1. set up the control register write, for now we'll always read
34603831d35Sstevel * channel 0, which is the only active 8591 port on the Nordica
34703831d35Sstevel * TODO: We'll need a minor node for each port that is used.
34803831d35Sstevel * 2. increment read count to read the throw-away byte
34903831d35Sstevel * 3. start the write/read of control/data registers
35003831d35Sstevel * 4. throw the first byte away
35103831d35Sstevel * 5. then return the data
35203831d35Sstevel */
35303831d35Sstevel
35403831d35Sstevel unitp->i2c_tran->i2c_flags = I2C_WR_RD;
35503831d35Sstevel unitp->i2c_tran->i2c_wlen = 1;
35603831d35Sstevel unitp->i2c_tran->i2c_wbuf[0] = (unitp->pcf8591_inprog |
35703831d35Sstevel channel);
35803831d35Sstevel /*
35903831d35Sstevel * read extra byte to throw away the first, (PCF8591 datasheet)
36003831d35Sstevel */
36103831d35Sstevel unitp->i2c_tran->i2c_rlen = bytes_to_rw + 1;
36203831d35Sstevel
36303831d35Sstevel if (nct_i2c_transfer(unitp->pcf8591_hdl,
36403831d35Sstevel unitp->i2c_tran) != I2C_SUCCESS) {
36503831d35Sstevel err = EIO;
36603831d35Sstevel } else {
36703831d35Sstevel /*
36803831d35Sstevel * Throw away the first byte according to PCF8591 datasheet
36903831d35Sstevel * If translating, use the second byte.
37003831d35Sstevel */
37103831d35Sstevel if (translate) {
37203831d35Sstevel unitp->i2c_tran->i2c_rbuf[0] =
37303831d35Sstevel translate_cputemp(unitp->i2c_tran->i2c_rbuf[1]);
37403831d35Sstevel } else {
37503831d35Sstevel unitp->i2c_tran->i2c_rbuf[0] =
37603831d35Sstevel unitp->i2c_tran->i2c_rbuf[1];
37703831d35Sstevel unitp->i2c_tran->i2c_rbuf[1] = 0;
37803831d35Sstevel }
37903831d35Sstevel
38003831d35Sstevel err = uiomove(unitp->i2c_tran->i2c_rbuf,
38103831d35Sstevel bytes_to_rw,
38203831d35Sstevel UIO_READ,
38303831d35Sstevel uiop);
38403831d35Sstevel }
38503831d35Sstevel mutex_enter(&unitp->umutex);
38603831d35Sstevel unitp->pcf8591_flags = 0;
38703831d35Sstevel cv_signal(&unitp->pcf8591_cv);
38803831d35Sstevel mutex_exit(&unitp->umutex);
38903831d35Sstevel
39003831d35Sstevel return (err);
39103831d35Sstevel }
39203831d35Sstevel
39303831d35Sstevel /*ARGSUSED*/
39403831d35Sstevel static int
pcf8591_read(dev_t dev,struct uio * uiop,cred_t * cred_p)39503831d35Sstevel pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
39603831d35Sstevel {
39703831d35Sstevel return (pcf8591_io(dev, uiop, B_READ));
39803831d35Sstevel }
39903831d35Sstevel
40003831d35Sstevel static int
call_copyin(caddr_t arg,struct pcf8591_unit * unitp,int mode)40103831d35Sstevel call_copyin(caddr_t arg, struct pcf8591_unit *unitp, int mode)
40203831d35Sstevel {
40303831d35Sstevel uchar_t *wbuf;
40403831d35Sstevel uchar_t *rbuf;
40503831d35Sstevel i2c_transfer_t i2ct;
40603831d35Sstevel i2c_transfer_t *i2ctp = unitp->i2c_tran;
40703831d35Sstevel
40803831d35Sstevel
40903831d35Sstevel if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
41003831d35Sstevel sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
41103831d35Sstevel return (I2C_FAILURE);
41203831d35Sstevel }
41303831d35Sstevel
41403831d35Sstevel /*
41503831d35Sstevel * Save the read and write buffer pointers in the transfer
41603831d35Sstevel * structure, otherwise these will get overwritten when we
41703831d35Sstevel * do a bcopy. Restore once done.
41803831d35Sstevel */
41903831d35Sstevel
42003831d35Sstevel wbuf = i2ctp->i2c_wbuf;
42103831d35Sstevel rbuf = i2ctp->i2c_rbuf;
42203831d35Sstevel
42303831d35Sstevel bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
42403831d35Sstevel
42503831d35Sstevel i2ctp->i2c_wbuf = wbuf;
42603831d35Sstevel i2ctp->i2c_rbuf = rbuf;
42703831d35Sstevel
42803831d35Sstevel /*
42903831d35Sstevel * copyin the read and write buffers to the saved buffers.
43003831d35Sstevel */
43103831d35Sstevel
43203831d35Sstevel if (i2ct.i2c_wlen != 0) {
43303831d35Sstevel if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
43403831d35Sstevel i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
43503831d35Sstevel return (I2C_FAILURE);
43603831d35Sstevel }
43703831d35Sstevel }
43803831d35Sstevel
43903831d35Sstevel return (I2C_SUCCESS);
44003831d35Sstevel }
44103831d35Sstevel
44203831d35Sstevel static int
call_copyout(caddr_t arg,struct pcf8591_unit * unitp,int mode)44303831d35Sstevel call_copyout(caddr_t arg, struct pcf8591_unit *unitp, int mode)
44403831d35Sstevel {
44503831d35Sstevel i2c_transfer_t i2ct;
44603831d35Sstevel i2c_transfer_t *i2ctp = unitp->i2c_tran;
44703831d35Sstevel uint16_t i2c_actlen;
44803831d35Sstevel
44903831d35Sstevel /*
45003831d35Sstevel * We will copyout the last three fields only, skipping
45103831d35Sstevel * the remaining ones, before copying the rbuf to the
45203831d35Sstevel * user buffer.
45303831d35Sstevel */
45403831d35Sstevel
45503831d35Sstevel int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
45603831d35Sstevel kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
45703831d35Sstevel
45803831d35Sstevel /*
45903831d35Sstevel * First copyin the user structure to the temporary i2ct,
46003831d35Sstevel * so that we have the wbuf and rbuf addresses in it.
46103831d35Sstevel */
46203831d35Sstevel
46303831d35Sstevel uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
46403831d35Sstevel
46503831d35Sstevel /*
46603831d35Sstevel * copyout the last three out fields now.
46703831d35Sstevel */
46803831d35Sstevel
46903831d35Sstevel if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
47003831d35Sstevel ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
47103831d35Sstevel != DDI_SUCCESS) {
47203831d35Sstevel return (I2C_FAILURE);
47303831d35Sstevel }
47403831d35Sstevel
47503831d35Sstevel /*
47603831d35Sstevel * In case we have something to write, get the address of the read
47703831d35Sstevel * buffer.
47803831d35Sstevel */
47903831d35Sstevel
48003831d35Sstevel if (i2ctp->i2c_rlen - i2ctp->i2c_r_resid > 0) {
48103831d35Sstevel
48203831d35Sstevel if (ddi_copyin((void *)arg, &i2ct,
48303831d35Sstevel sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
48403831d35Sstevel return (I2C_FAILURE);
48503831d35Sstevel }
48603831d35Sstevel
48703831d35Sstevel /*
48803831d35Sstevel * copyout the read buffer to the saved user buffer in i2ct.
48903831d35Sstevel */
49003831d35Sstevel
49103831d35Sstevel i2c_actlen = i2ctp->i2c_rlen - i2ctp->i2c_r_resid;
49203831d35Sstevel if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
49303831d35Sstevel i2c_actlen, mode) != DDI_SUCCESS) {
49403831d35Sstevel return (I2C_FAILURE);
49503831d35Sstevel }
49603831d35Sstevel }
49703831d35Sstevel
49803831d35Sstevel return (I2C_SUCCESS);
49903831d35Sstevel }
50003831d35Sstevel
50103831d35Sstevel /*
50203831d35Sstevel * The ioctls will use the same name as the Javelin ioctls. We
50303831d35Sstevel * will have a very restricted set for MC, and unlike Javelin
50403831d35Sstevel * will not have a envctrl_chip structure to return values
50503831d35Sstevel * from the driver. All we will have is a uint8_t value to
50603831d35Sstevel * get or set values from the driver. Also, unlike the Javelin,
50703831d35Sstevel * where 'index' is used to specify the input port from where
50803831d35Sstevel * temperature is collected, here different minor nodes will be
50903831d35Sstevel * created by the driver for each port, eliminating the need for
51003831d35Sstevel * 'index' - leaving us with only the value to pass.
51103831d35Sstevel */
51203831d35Sstevel
51303831d35Sstevel /*ARGSUSED*/
51403831d35Sstevel static int
pcf8591_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)51503831d35Sstevel pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
51603831d35Sstevel cred_t *credp, int *rvalp)
51703831d35Sstevel {
51803831d35Sstevel int err = 0;
51903831d35Sstevel struct pcf8591_unit *unitp;
52003831d35Sstevel minor_t minor = getminor(dev);
52103831d35Sstevel
52203831d35Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
52303831d35Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
52403831d35Sstevel
52503831d35Sstevel unitp = (struct pcf8591_unit *)
52603831d35Sstevel ddi_get_soft_state(pcf8591_soft_statep, instance);
52703831d35Sstevel
52803831d35Sstevel mutex_enter(&unitp->umutex);
52903831d35Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
53003831d35Sstevel if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
53103831d35Sstevel mutex_exit(&unitp->umutex);
53203831d35Sstevel
53303831d35Sstevel return (EINTR);
53403831d35Sstevel }
53503831d35Sstevel }
53603831d35Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
53703831d35Sstevel mutex_exit(&unitp->umutex);
53803831d35Sstevel
53903831d35Sstevel switch (cmd) {
54003831d35Sstevel
54103831d35Sstevel case ENVC_IOC_GETTEMP: {
54203831d35Sstevel /*
54303831d35Sstevel * Read the status byte from pcf8591 chip. The value will
54403831d35Sstevel * be already converted to Celcius by translate_cputemp.
54503831d35Sstevel */
54607d06da5SSurya Prakki (void) pcf8591_read_chip(unitp, channel, 1);
54703831d35Sstevel if (ddi_copyout(unitp->i2c_tran->i2c_rbuf,
54803831d35Sstevel (caddr_t)arg, sizeof (uint8_t), mode) != DDI_SUCCESS) {
54903831d35Sstevel err = EFAULT;
55003831d35Sstevel }
55103831d35Sstevel break;
55203831d35Sstevel }
55303831d35Sstevel
55403831d35Sstevel case ENVC_IOC_GETMODE: {
55503831d35Sstevel uint8_t curr_mode = unitp->current_mode;
55603831d35Sstevel
55703831d35Sstevel if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
55803831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) {
55903831d35Sstevel err = EFAULT;
56003831d35Sstevel }
56103831d35Sstevel break;
56203831d35Sstevel }
56303831d35Sstevel
56403831d35Sstevel case ENVC_IOC_SETMODE: {
56503831d35Sstevel uint8_t curr_mode;
56603831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
56703831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) {
56803831d35Sstevel err = EFAULT;
56903831d35Sstevel break;
57003831d35Sstevel }
57103831d35Sstevel if (curr_mode == ENVCTRL_DIAG_MODE ||
57203831d35Sstevel curr_mode == ENVCTRL_NORMAL_MODE) {
57303831d35Sstevel unitp->current_mode = curr_mode; /* Don't do anything */
57403831d35Sstevel }
57503831d35Sstevel break;
57603831d35Sstevel }
57703831d35Sstevel
57803831d35Sstevel /* Testing, may be removed */
57903831d35Sstevel case I2CDEV_TRAN:
58003831d35Sstevel if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
58103831d35Sstevel err = EFAULT;
58203831d35Sstevel break;
58303831d35Sstevel }
58403831d35Sstevel if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
58503831d35Sstevel != I2C_SUCCESS) {
58603831d35Sstevel err = EFAULT;
58703831d35Sstevel break;
58803831d35Sstevel }
58903831d35Sstevel if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
59003831d35Sstevel err = EFAULT;
59103831d35Sstevel break;
59203831d35Sstevel }
59303831d35Sstevel break;
59403831d35Sstevel
59503831d35Sstevel /*
59603831d35Sstevel * TESTING TRANSLATION from "adc" "table" property
59703831d35Sstevel * translate thermistor index into temp Celcius
59803831d35Sstevel */
59903831d35Sstevel case I2CDEV_GETTEMP: {
60003831d35Sstevel struct i2c_transfer *tp;
60103831d35Sstevel if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
60203831d35Sstevel err = EFAULT;
60303831d35Sstevel break;
60403831d35Sstevel }
60503831d35Sstevel tp = unitp->i2c_tran;
60603831d35Sstevel if (tp->i2c_rlen != 1) {
60703831d35Sstevel err = EINVAL;
60803831d35Sstevel break;
60903831d35Sstevel }
61003831d35Sstevel /*
61103831d35Sstevel * Throw away the first byte according to PCF8591 datasheet,
61203831d35Sstevel * so read two bytes
61303831d35Sstevel */
61403831d35Sstevel tp->i2c_rlen = 2;
61503831d35Sstevel if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
61603831d35Sstevel != I2C_SUCCESS) {
61703831d35Sstevel err = EFAULT;
61803831d35Sstevel break;
61903831d35Sstevel }
62003831d35Sstevel #ifdef DEBUG
62103831d35Sstevel if (pcf8591_debug & 0x0010)
62203831d35Sstevel cmn_err(CE_NOTE,
62319397407SSherry Moore "pcf8591_ioctl: i2c_rlen=%d; "
62419397407SSherry Moore "i2c_rbuf[0,1]=0x%x,0x%x\n",
62503831d35Sstevel tp->i2c_rlen, tp->i2c_rbuf[0], tp->i2c_rbuf[1]);
62603831d35Sstevel #endif /* DEBUG */
62703831d35Sstevel /*
62803831d35Sstevel * Throw away the first byte according to PCF8591 datasheet
62903831d35Sstevel */
63003831d35Sstevel if ((tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]))
63103831d35Sstevel == 0) {
63203831d35Sstevel err = EINVAL;
63303831d35Sstevel break;
63403831d35Sstevel }
63503831d35Sstevel tp->i2c_rbuf[1] = 0;
63603831d35Sstevel
63703831d35Sstevel if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
63803831d35Sstevel err = EFAULT;
63903831d35Sstevel break;
64003831d35Sstevel }
64103831d35Sstevel break;
64203831d35Sstevel }
64303831d35Sstevel
64403831d35Sstevel case I2CDEV_GETTABLES: {
64503831d35Sstevel break;
64603831d35Sstevel }
64703831d35Sstevel default:
64803831d35Sstevel err = EINVAL;
64903831d35Sstevel }
65003831d35Sstevel
65103831d35Sstevel mutex_enter(&unitp->umutex);
65203831d35Sstevel unitp->pcf8591_flags = 0;
65303831d35Sstevel cv_signal(&unitp->pcf8591_cv);
65403831d35Sstevel mutex_exit(&unitp->umutex);
65503831d35Sstevel
65603831d35Sstevel return (err);
65703831d35Sstevel }
65803831d35Sstevel
65903831d35Sstevel static int
pcf8591_do_detach(dev_info_t * dip)66003831d35Sstevel pcf8591_do_detach(dev_info_t *dip)
66103831d35Sstevel {
66203831d35Sstevel register struct pcf8591_unit *unitp;
66303831d35Sstevel int instance;
66403831d35Sstevel uint_t attach_flag;
66503831d35Sstevel
66603831d35Sstevel instance = ddi_get_instance(dip);
66703831d35Sstevel unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
66803831d35Sstevel attach_flag = unitp->attach_flag;
66903831d35Sstevel
67003831d35Sstevel if (attach_flag & PCF8591_KSTAT_INIT) {
67103831d35Sstevel pcf8591_delete_kstats(unitp);
67203831d35Sstevel }
67303831d35Sstevel
67403831d35Sstevel if (attach_flag & PCF8591_LOCK_INIT) {
67503831d35Sstevel mutex_destroy(&unitp->umutex);
67603831d35Sstevel cv_destroy(&unitp->pcf8591_cv);
67703831d35Sstevel }
67803831d35Sstevel
67903831d35Sstevel /*
68003831d35Sstevel * Restore the lengths of the rbuf and wbuf, which was originally
68103831d35Sstevel * allocated so that the appropriate amount of rbuf and wbuf are
68203831d35Sstevel * freed.
68303831d35Sstevel */
68403831d35Sstevel if (attach_flag & PCF8591_ALLOC_TRANSFER) {
68503831d35Sstevel unitp->i2c_tran->i2c_wlen = MAX_WLEN;
68603831d35Sstevel unitp->i2c_tran->i2c_rlen = MAX_RLEN;
68703831d35Sstevel i2c_transfer_free(unitp->pcf8591_hdl, unitp->i2c_tran);
68803831d35Sstevel }
68903831d35Sstevel
69003831d35Sstevel if (attach_flag & PCF8591_REGISTER_CLIENT) {
69103831d35Sstevel i2c_client_unregister(unitp->pcf8591_hdl);
69203831d35Sstevel }
69303831d35Sstevel
69403831d35Sstevel if (attach_flag & PCF8591_MINORS_CREATED) {
69503831d35Sstevel ddi_remove_minor_node(dip, NULL);
69603831d35Sstevel }
69703831d35Sstevel
69803831d35Sstevel /*
69903831d35Sstevel * Free the memory allocated for the properties.
70003831d35Sstevel */
70103831d35Sstevel if (attach_flag & PCF8591_PROPS_READ) {
70203831d35Sstevel ddi_prop_free(unitp->props.name);
70303831d35Sstevel if (unitp->props.num_chans_used) {
70403831d35Sstevel ddi_prop_free(unitp->props.channels_in_use);
70503831d35Sstevel }
70603831d35Sstevel
70703831d35Sstevel if (unitp->props.channels_description) {
70803831d35Sstevel ddi_prop_free(unitp->props.channels_description);
70903831d35Sstevel }
71003831d35Sstevel }
71103831d35Sstevel
71203831d35Sstevel if (attach_flag & PCF8591_SOFT_STATE_ALLOC) {
71303831d35Sstevel ddi_soft_state_free(pcf8591_soft_statep, instance);
71403831d35Sstevel }
71503831d35Sstevel
71603831d35Sstevel return (DDI_SUCCESS);
71703831d35Sstevel }
71803831d35Sstevel
71903831d35Sstevel static int
pcf8591_do_suspend(dev_info_t * dip)72003831d35Sstevel pcf8591_do_suspend(dev_info_t *dip)
72103831d35Sstevel {
72203831d35Sstevel int instance = ddi_get_instance(dip);
72303831d35Sstevel struct pcf8591_unit *unitp = (struct pcf8591_unit *)
72403831d35Sstevel ddi_get_soft_state(pcf8591_soft_statep, instance);
72503831d35Sstevel
72603831d35Sstevel if (unitp == NULL) {
72703831d35Sstevel return (ENXIO);
72803831d35Sstevel }
72903831d35Sstevel
73003831d35Sstevel /*
73103831d35Sstevel * Set the busy flag so that future transactions block
73203831d35Sstevel * until resume.
73303831d35Sstevel */
73403831d35Sstevel mutex_enter(&unitp->umutex);
73503831d35Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
73603831d35Sstevel if (cv_wait_sig(&unitp->pcf8591_cv,
73703831d35Sstevel &unitp->umutex) <= 0) {
73803831d35Sstevel mutex_exit(&unitp->umutex);
73903831d35Sstevel
74003831d35Sstevel return (DDI_FAILURE);
74103831d35Sstevel }
74203831d35Sstevel }
74303831d35Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
74403831d35Sstevel mutex_exit(&unitp->umutex);
74503831d35Sstevel
74603831d35Sstevel return (DDI_SUCCESS);
74703831d35Sstevel }
74803831d35Sstevel
74903831d35Sstevel static int
pcf8591_do_resume(dev_info_t * dip)75003831d35Sstevel pcf8591_do_resume(dev_info_t *dip)
75103831d35Sstevel {
75203831d35Sstevel int instance = ddi_get_instance(dip);
75303831d35Sstevel struct pcf8591_unit *unitp = (struct pcf8591_unit *)
75403831d35Sstevel ddi_get_soft_state(pcf8591_soft_statep, instance);
75503831d35Sstevel if (unitp == NULL) {
75603831d35Sstevel return (ENXIO);
75703831d35Sstevel }
75803831d35Sstevel
75903831d35Sstevel mutex_enter(&unitp->umutex);
76003831d35Sstevel unitp->pcf8591_flags = 0;
76103831d35Sstevel cv_signal(&unitp->pcf8591_cv);
76203831d35Sstevel mutex_exit(&unitp->umutex);
76303831d35Sstevel
76403831d35Sstevel return (DDI_SUCCESS);
76503831d35Sstevel }
76603831d35Sstevel
76703831d35Sstevel static int
pcf8591_do_attach(dev_info_t * dip)76803831d35Sstevel pcf8591_do_attach(dev_info_t *dip)
76903831d35Sstevel {
77003831d35Sstevel register struct pcf8591_unit *unitp;
77103831d35Sstevel int i, instance;
77203831d35Sstevel char name[MAXNAMELEN];
77303831d35Sstevel minor_t minor;
77403831d35Sstevel
77503831d35Sstevel instance = ddi_get_instance(dip);
77603831d35Sstevel
77703831d35Sstevel if (ddi_soft_state_zalloc(pcf8591_soft_statep, instance) != 0) {
77803831d35Sstevel return (DDI_FAILURE);
77903831d35Sstevel }
78003831d35Sstevel
78103831d35Sstevel unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
78203831d35Sstevel
78303831d35Sstevel if (unitp == NULL) {
78403831d35Sstevel return (DDI_FAILURE);
78503831d35Sstevel }
78603831d35Sstevel
78703831d35Sstevel unitp->dip = dip;
78803831d35Sstevel
78903831d35Sstevel unitp->attach_flag = PCF8591_SOFT_STATE_ALLOC;
79003831d35Sstevel
79103831d35Sstevel if (pcf8591_read_props(unitp) != DDI_PROP_SUCCESS) {
79207d06da5SSurya Prakki (void) pcf8591_do_detach(dip);
79303831d35Sstevel return (DDI_FAILURE);
79403831d35Sstevel }
79503831d35Sstevel
79603831d35Sstevel unitp->attach_flag |= PCF8591_PROPS_READ;
79703831d35Sstevel
79803831d35Sstevel /*
79903831d35Sstevel * Set the current operating mode to NORMAL_MODE.
80003831d35Sstevel */
80103831d35Sstevel unitp->current_mode = ENVCTRL_NORMAL_MODE; /* normal mode */
80203831d35Sstevel
80307d06da5SSurya Prakki (void) snprintf(unitp->pcf8591_name, PCF8591_NAMELEN,
80403831d35Sstevel "%s%d", ddi_driver_name(dip), instance);
80503831d35Sstevel
80603831d35Sstevel /*
80703831d35Sstevel * Create a minor node corresponding to channel 0 to 3
80803831d35Sstevel */
80903831d35Sstevel for (i = 0; i < PCF8591_MAX_CHANS; i++) {
81003831d35Sstevel if (i == 0) {
81103831d35Sstevel (void) sprintf(name, "cputemp");
81203831d35Sstevel } else {
81303831d35Sstevel (void) sprintf(name, "%d", i);
81403831d35Sstevel }
81503831d35Sstevel minor = PCF8591_MINOR_NUM(instance, i);
81603831d35Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
81703831d35Sstevel PCF8591_NODE_TYPE, NULL) == DDI_FAILURE) {
81803831d35Sstevel ddi_remove_minor_node(dip, NULL);
81907d06da5SSurya Prakki (void) pcf8591_do_detach(dip);
82003831d35Sstevel return (DDI_FAILURE);
82103831d35Sstevel }
82203831d35Sstevel }
82303831d35Sstevel
82403831d35Sstevel unitp->attach_flag |= PCF8591_MINORS_CREATED;
82503831d35Sstevel
82603831d35Sstevel if (i2c_client_register(dip, &unitp->pcf8591_hdl)
82703831d35Sstevel != I2C_SUCCESS) {
82807d06da5SSurya Prakki (void) pcf8591_do_detach(dip);
82903831d35Sstevel return (DDI_FAILURE);
83003831d35Sstevel }
83103831d35Sstevel
83203831d35Sstevel unitp->attach_flag |= PCF8591_REGISTER_CLIENT;
83303831d35Sstevel
83403831d35Sstevel /*
83503831d35Sstevel * We allocate a single i2c_transfer_t structure for all
83603831d35Sstevel * i2c transactions.
83703831d35Sstevel */
83803831d35Sstevel if (i2c_transfer_alloc(unitp->pcf8591_hdl, &unitp->i2c_tran,
83903831d35Sstevel MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
84007d06da5SSurya Prakki (void) pcf8591_do_detach(dip);
84103831d35Sstevel return (DDI_FAILURE);
84203831d35Sstevel }
84303831d35Sstevel
84403831d35Sstevel unitp->attach_flag |= PCF8591_ALLOC_TRANSFER;
84503831d35Sstevel
84603831d35Sstevel /*
84703831d35Sstevel * The flags will be set to I2C_WR because for all reads from
84803831d35Sstevel * the 8591 we need to also write the control byte.
84903831d35Sstevel */
85003831d35Sstevel unitp->i2c_tran->i2c_flags = I2C_WR;
85103831d35Sstevel unitp->i2c_tran->i2c_version = I2C_XFER_REV;
85203831d35Sstevel
85303831d35Sstevel
85403831d35Sstevel /*
85503831d35Sstevel * Set the analog programming mode to default. Upper nibble
85603831d35Sstevel * in control byte. Four single ended inputs, output not enabled.
85703831d35Sstevel */
85803831d35Sstevel unitp->pcf8591_inprog = PCF8591_4SINGLE | PCF8591_ANALOG_INPUT_EN;
85903831d35Sstevel
86003831d35Sstevel /*
86103831d35Sstevel * Set the open flag for each channel to 0.
86203831d35Sstevel */
86303831d35Sstevel for (i = 0; i < PCF8591_MAX_CHANS; i++) {
86403831d35Sstevel unitp->pcf8591_oflag[i] = 0;
86503831d35Sstevel }
86603831d35Sstevel
86703831d35Sstevel /*
86803831d35Sstevel * Set the busy flag to 0.
86903831d35Sstevel */
87003831d35Sstevel unitp->pcf8591_flags = 0;
87103831d35Sstevel
87203831d35Sstevel mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
87303831d35Sstevel cv_init(&unitp->pcf8591_cv, NULL, CV_DRIVER, NULL);
87403831d35Sstevel
87503831d35Sstevel unitp->attach_flag |= PCF8591_LOCK_INIT;
87603831d35Sstevel
87703831d35Sstevel if (pcf8591_add_kstats(unitp) != DDI_SUCCESS) {
87807d06da5SSurya Prakki (void) pcf8591_do_detach(dip);
87903831d35Sstevel return (DDI_FAILURE);
88003831d35Sstevel }
88103831d35Sstevel
88203831d35Sstevel unitp->attach_flag |= PCF8591_KSTAT_INIT;
88303831d35Sstevel
88403831d35Sstevel ddi_report_dev(dip);
88503831d35Sstevel
88603831d35Sstevel return (DDI_SUCCESS);
88703831d35Sstevel }
88803831d35Sstevel
88903831d35Sstevel /* ARGSUSED */
89003831d35Sstevel static int
pcf8591_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)89103831d35Sstevel pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
89203831d35Sstevel {
89303831d35Sstevel dev_t dev;
89403831d35Sstevel int instance;
89503831d35Sstevel
89603831d35Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) {
89703831d35Sstevel dev = (dev_t)arg;
89803831d35Sstevel instance = PCF8591_MINOR_TO_DEVINST(getminor(dev));
89903831d35Sstevel *result = (void *)(uintptr_t)instance;
90003831d35Sstevel return (DDI_SUCCESS);
90103831d35Sstevel }
90203831d35Sstevel return (DDI_FAILURE);
90303831d35Sstevel }
90403831d35Sstevel
90503831d35Sstevel static int
pcf8591_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)90603831d35Sstevel pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
90703831d35Sstevel {
90803831d35Sstevel switch (cmd) {
90903831d35Sstevel case DDI_ATTACH:
91003831d35Sstevel return (pcf8591_do_attach(dip));
91103831d35Sstevel case DDI_RESUME:
91203831d35Sstevel return (pcf8591_do_resume(dip));
91303831d35Sstevel default:
91403831d35Sstevel return (DDI_FAILURE);
91503831d35Sstevel }
91603831d35Sstevel }
91703831d35Sstevel
91803831d35Sstevel static int
pcf8591_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)91903831d35Sstevel pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
92003831d35Sstevel {
92103831d35Sstevel switch (cmd) {
92203831d35Sstevel case DDI_DETACH:
92303831d35Sstevel return (pcf8591_do_detach(dip));
92403831d35Sstevel case DDI_SUSPEND:
92503831d35Sstevel return (pcf8591_do_suspend(dip));
92603831d35Sstevel default:
92703831d35Sstevel return (DDI_FAILURE);
92803831d35Sstevel }
92903831d35Sstevel }
93003831d35Sstevel
93103831d35Sstevel static uint8_t
translate_cputemp(uint8_t value)93203831d35Sstevel translate_cputemp(uint8_t value)
93303831d35Sstevel {
93403831d35Sstevel return (_cpu_temps[value]);
93503831d35Sstevel }
93603831d35Sstevel
93703831d35Sstevel static int
pcf8591_add_kstats(struct pcf8591_unit * unitp)93803831d35Sstevel pcf8591_add_kstats(struct pcf8591_unit *unitp)
93903831d35Sstevel {
94003831d35Sstevel if ((unitp->tempksp = kstat_create(I2C_PCF8591_NAME,
94103831d35Sstevel unitp->instance, I2C_KSTAT_CPUTEMP, "misc",
94203831d35Sstevel KSTAT_TYPE_RAW, sizeof (unitp->temp_kstats),
94303831d35Sstevel KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
94403831d35Sstevel
94503831d35Sstevel return (DDI_FAILURE);
94603831d35Sstevel }
94703831d35Sstevel
94803831d35Sstevel /*
94903831d35Sstevel * The kstat fields are already initialized in the attach routine..
95003831d35Sstevel */
95103831d35Sstevel
95203831d35Sstevel unitp->tempksp->ks_update = pcf8591_temp_kstat_update;
95303831d35Sstevel unitp->tempksp->ks_private = (void *)unitp;
95403831d35Sstevel
95507d06da5SSurya Prakki (void) strcpy(unitp->temp_kstats.label,
95603831d35Sstevel unitp->props.channels_description[0]);
95703831d35Sstevel unitp->temp_kstats.type = ENVC_NETRACT_CPU_SENSOR;
95803831d35Sstevel
95903831d35Sstevel kstat_install(unitp->tempksp);
96003831d35Sstevel
96103831d35Sstevel return (DDI_SUCCESS);
96203831d35Sstevel }
96303831d35Sstevel
96403831d35Sstevel static void
pcf8591_delete_kstats(struct pcf8591_unit * unitp)96503831d35Sstevel pcf8591_delete_kstats(struct pcf8591_unit *unitp)
96603831d35Sstevel {
96703831d35Sstevel kstat_delete(unitp->tempksp);
96803831d35Sstevel }
96903831d35Sstevel
97003831d35Sstevel static int
pcf8591_temp_kstat_update(kstat_t * ksp,int rw)97103831d35Sstevel pcf8591_temp_kstat_update(kstat_t *ksp, int rw)
97203831d35Sstevel {
97303831d35Sstevel struct pcf8591_unit *unitp;
97403831d35Sstevel char *kstatp;
97503831d35Sstevel int err = 0;
97603831d35Sstevel int channel = 0;
97703831d35Sstevel int warn_temp = 0;
97803831d35Sstevel int shutdown_temp = 0;
97903831d35Sstevel
98003831d35Sstevel unitp = (struct pcf8591_unit *)ksp->ks_private;
98103831d35Sstevel
98203831d35Sstevel mutex_enter(&unitp->umutex);
98303831d35Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
98403831d35Sstevel if (cv_wait_sig(&unitp->pcf8591_cv,
98503831d35Sstevel &unitp->umutex) <= 0) {
98603831d35Sstevel mutex_exit(&unitp->umutex);
98703831d35Sstevel
98803831d35Sstevel return (EINTR);
98903831d35Sstevel }
99003831d35Sstevel }
99103831d35Sstevel
99203831d35Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
99303831d35Sstevel mutex_exit(&unitp->umutex);
99403831d35Sstevel
99503831d35Sstevel kstatp = (char *)ksp->ks_data;
99603831d35Sstevel
99703831d35Sstevel if (rw == KSTAT_WRITE) {
99803831d35Sstevel
99903831d35Sstevel /* check for the size of buffer */
100003831d35Sstevel if (ksp->ks_data_size != sizeof (unitp->temp_kstats)) {
100103831d35Sstevel err = EIO;
100203831d35Sstevel goto bail;
100303831d35Sstevel }
100403831d35Sstevel
100503831d35Sstevel warn_temp = ((envctrl_temp_t *)kstatp)->warning_threshold;
100603831d35Sstevel shutdown_temp = ((envctrl_temp_t *)kstatp)->shutdown_threshold;
100703831d35Sstevel
100803831d35Sstevel if (shutdown_temp < SHUTDOWN_TEMP_MIN || shutdown_temp >
100903831d35Sstevel SHUTDOWN_TEMP_MAX) {
101003831d35Sstevel err = EIO;
101103831d35Sstevel goto bail;
101203831d35Sstevel }
101303831d35Sstevel
101403831d35Sstevel if (warn_temp < 0 || shutdown_temp <= warn_temp) {
101503831d35Sstevel err = EIO;
101603831d35Sstevel goto bail;
101703831d35Sstevel }
101803831d35Sstevel
101903831d35Sstevel /* write into kstat fields */
102003831d35Sstevel unitp->temp_kstats.warning_threshold = warn_temp;
102103831d35Sstevel unitp->temp_kstats.shutdown_threshold = shutdown_temp;
102203831d35Sstevel
102303831d35Sstevel } else {
102407d06da5SSurya Prakki (void) pcf8591_read_chip(unitp, channel, 1);
102503831d35Sstevel unitp->temp_kstats.value =
102603831d35Sstevel unitp->i2c_tran->i2c_rbuf[0];
102703831d35Sstevel bcopy((caddr_t)&unitp->temp_kstats, kstatp,
102803831d35Sstevel sizeof (unitp->temp_kstats));
102903831d35Sstevel }
103003831d35Sstevel
103103831d35Sstevel bail:
103203831d35Sstevel
103303831d35Sstevel mutex_enter(&unitp->umutex);
103403831d35Sstevel unitp->pcf8591_flags = 0;
103503831d35Sstevel cv_signal(&unitp->pcf8591_cv);
103603831d35Sstevel mutex_exit(&unitp->umutex);
103703831d35Sstevel
103803831d35Sstevel return (err);
103903831d35Sstevel }
104003831d35Sstevel
104103831d35Sstevel static int
pcf8591_read_chip(struct pcf8591_unit * unitp,uint8_t channel,int size)104203831d35Sstevel pcf8591_read_chip(struct pcf8591_unit *unitp, uint8_t channel,
104303831d35Sstevel int size)
104403831d35Sstevel {
104503831d35Sstevel int retval = I2C_SUCCESS;
104603831d35Sstevel
104703831d35Sstevel /*
104803831d35Sstevel * We need to read an extra byte, since as per specification
104903831d35Sstevel * the first byte read should be discarded.
105003831d35Sstevel */
105103831d35Sstevel i2c_transfer_t *tp = unitp->i2c_tran;
105203831d35Sstevel tp->i2c_flags = I2C_WR_RD;
105303831d35Sstevel tp->i2c_rlen = size+1;
105403831d35Sstevel tp->i2c_wlen = 1;
105503831d35Sstevel tp->i2c_wbuf[0] = (unitp->pcf8591_inprog |
105603831d35Sstevel channel);
105703831d35Sstevel
105803831d35Sstevel retval = nct_i2c_transfer(unitp->pcf8591_hdl, tp);
105903831d35Sstevel if (retval == I2C_SUCCESS) {
106003831d35Sstevel tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]);
106103831d35Sstevel }
106203831d35Sstevel
106303831d35Sstevel if (tp->i2c_rbuf[0] == 0) {
106403831d35Sstevel retval = I2C_FAILURE;
106503831d35Sstevel }
106603831d35Sstevel
106703831d35Sstevel return (retval);
106803831d35Sstevel }
106903831d35Sstevel
107003831d35Sstevel /*
107103831d35Sstevel * Reads the properties of the pcf8591 device.
107203831d35Sstevel */
107303831d35Sstevel static int
pcf8591_read_props(struct pcf8591_unit * unitp)107403831d35Sstevel pcf8591_read_props(struct pcf8591_unit *unitp)
107503831d35Sstevel {
107603831d35Sstevel dev_info_t *dip = unitp->dip;
107703831d35Sstevel int i, retval = 0, prop_len;
107803831d35Sstevel int instance = ddi_get_instance(dip);
107903831d35Sstevel int warning_temp, shutdown_temp;
108003831d35Sstevel uint32_t *prop_value = NULL;
108103831d35Sstevel uchar_t *creg_prop;
108203831d35Sstevel char *function;
108303831d35Sstevel uint_t tblsz;
108403831d35Sstevel
108503831d35Sstevel #ifdef lint
108603831d35Sstevel instance = instance;
108703831d35Sstevel #endif
108803831d35Sstevel /*
108903831d35Sstevel * Check for the pcf8591_function property, and make sure it's
109003831d35Sstevel * cputemp.
109103831d35Sstevel */
109203831d35Sstevel if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
109303831d35Sstevel "pcf8591_function", &function) != DDI_SUCCESS) {
109403831d35Sstevel dbg_print(CE_WARN, "Couldn't find pcf8591_function property");
109503831d35Sstevel
109603831d35Sstevel return (DDI_FAILURE);
109703831d35Sstevel }
109803831d35Sstevel
109903831d35Sstevel if (strcmp(function, "cputemp") != 0) {
110003831d35Sstevel dbg_print(CE_WARN, "pcf8591_function is not cputemp");
110103831d35Sstevel ddi_prop_free(function);
110203831d35Sstevel
110303831d35Sstevel return (DDI_FAILURE);
110403831d35Sstevel }
110503831d35Sstevel
110603831d35Sstevel ddi_prop_free(function);
110703831d35Sstevel
110803831d35Sstevel retval = ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
110903831d35Sstevel "name", &unitp->props.name);
111003831d35Sstevel if (retval != DDI_PROP_SUCCESS) {
111103831d35Sstevel
111203831d35Sstevel return (retval);
111303831d35Sstevel }
111403831d35Sstevel #ifdef DEBUG
111503831d35Sstevel else if (pcf8591_debug & 0x02)
111603831d35Sstevel cmn_err(CE_NOTE,
111703831d35Sstevel "pcf8591_read_props:ddi_prop_lookup_string(%s): \
111803831d35Sstevel found %s ", "name", unitp->props.name);
111903831d35Sstevel #endif /* DEBUG */
112003831d35Sstevel
112103831d35Sstevel retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
112203831d35Sstevel DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
112303831d35Sstevel "reg", (caddr_t)&prop_value, &prop_len);
112403831d35Sstevel if (retval == DDI_PROP_SUCCESS) {
112503831d35Sstevel unitp->props.i2c_bus = (uint16_t)prop_value[0];
112603831d35Sstevel unitp->props.slave_address = (uint16_t)prop_value[1];
112703831d35Sstevel kmem_free(prop_value, prop_len);
112803831d35Sstevel #ifdef DEBUG
112903831d35Sstevel if (pcf8591_debug & 0x02)
113003831d35Sstevel cmn_err(CE_NOTE,
113103831d35Sstevel "pcf8591:ddi_getlongprop(%s) returns %d,"
113203831d35Sstevel " i2c_bus,slave=0x%x,0x%x",
113303831d35Sstevel "reg", retval, unitp->props.i2c_bus,
113403831d35Sstevel unitp->props.slave_address);
113503831d35Sstevel #endif /* DEBUG */
113603831d35Sstevel } else {
113703831d35Sstevel unitp->props.i2c_bus = (uint16_t)-1;
113803831d35Sstevel unitp->props.slave_address = (uint16_t)-1;
113903831d35Sstevel #ifdef DEBUG
114003831d35Sstevel cmn_err(CE_WARN,
114103831d35Sstevel "pcf8591_read_props:ddi_getlongprop(%s) returns %d,"
114203831d35Sstevel " default it to 0x%x:0x%X",
114303831d35Sstevel "reg", retval, unitp->props.i2c_bus,
114403831d35Sstevel unitp->props.slave_address);
114503831d35Sstevel #endif /* DEBUG */
114603831d35Sstevel }
114707d06da5SSurya Prakki (void) ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
114803831d35Sstevel "channels-in-use", &prop_len);
114903831d35Sstevel retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
115003831d35Sstevel dip, DDI_PROP_DONTPASS,
115103831d35Sstevel "channels-in-use",
115203831d35Sstevel (uchar_t **)&unitp->props.channels_in_use,
115303831d35Sstevel &unitp->props.num_chans_used);
115403831d35Sstevel if (retval == DDI_PROP_SUCCESS) {
115503831d35Sstevel unitp->props.num_chans_used /= sizeof (pcf8591_channel_t);
115603831d35Sstevel } else {
115703831d35Sstevel unitp->props.num_chans_used = 0;
115803831d35Sstevel }
115903831d35Sstevel
116003831d35Sstevel #ifdef DEBUG
116103831d35Sstevel if (pcf8591_debug & 0x0002)
116203831d35Sstevel cmn_err(CE_NOTE,
116303831d35Sstevel "pcf8591_read_props:ddi_prop_lookup_byte_array(%s)"
116403831d35Sstevel "returns %d\n"
116503831d35Sstevel "\t\tlength=%d, #elements=%d",
116603831d35Sstevel "channels-in-use", retval,
116703831d35Sstevel prop_len, unitp->props.num_chans_used);
116803831d35Sstevel #endif /* DEBUG */
116903831d35Sstevel
117003831d35Sstevel retval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
117103831d35Sstevel DDI_PROP_DONTPASS, "channels-description",
117203831d35Sstevel (char ***)&unitp->props.channels_description,
117303831d35Sstevel (uint_t *)&prop_len);
117403831d35Sstevel
117503831d35Sstevel if (retval != DDI_PROP_SUCCESS) {
117603831d35Sstevel prop_len = 0;
117703831d35Sstevel unitp->props.channels_description = NULL;
117803831d35Sstevel }
117903831d35Sstevel
118003831d35Sstevel #ifdef DEBUG
118103831d35Sstevel if (pcf8591_debug & 0x0002) {
118203831d35Sstevel cmn_err(CE_NOTE,
118303831d35Sstevel "pcf8591_read_props:ddi_prop_lookup_string_array(%s)"
118403831d35Sstevel "returns %d, length=%d",
118503831d35Sstevel "channels-description", retval, prop_len);
118603831d35Sstevel for (i = 0; i < prop_len; ++i) {
118703831d35Sstevel cmn_err(CE_NOTE, "channels-description[%d]=<%s>",
118803831d35Sstevel i, unitp->props.channels_description[i]);
118903831d35Sstevel }
119003831d35Sstevel }
119103831d35Sstevel #endif /* DEBUG */
119203831d35Sstevel
119303831d35Sstevel /*
119403831d35Sstevel * The following code was borrowed from envctrltwo.c
119503831d35Sstevel * I haven't yet investigated why the copy target is index + 2
119603831d35Sstevel */
119703831d35Sstevel retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
119803831d35Sstevel DDI_PROP_DONTPASS, "tables", &creg_prop, (uint_t *)&prop_len);
119903831d35Sstevel
120003831d35Sstevel if (retval != DDI_PROP_SUCCESS) {
120103831d35Sstevel #ifdef DEBUG
120203831d35Sstevel cmn_err(CE_WARN, "%s%d: Unable to read pcf8591 tables property",
120303831d35Sstevel ddi_get_name(dip), instance);
120403831d35Sstevel #endif /* DEBUG */
120503831d35Sstevel
120603831d35Sstevel return (DDI_NOT_WELL_FORMED);
120703831d35Sstevel }
120803831d35Sstevel
120903831d35Sstevel tblsz = (sizeof (_cpu_temps) / sizeof (uchar_t));
121003831d35Sstevel if (prop_len <= tblsz) {
121103831d35Sstevel for (i = 0; i < prop_len; i++) {
121203831d35Sstevel _cpu_temps[i] = creg_prop[i];
121303831d35Sstevel }
121403831d35Sstevel }
121503831d35Sstevel #ifdef DEBUG
121603831d35Sstevel if (pcf8591_debug & 0x0002)
121703831d35Sstevel cmn_err(CE_NOTE, "pcf8591_read_props: _cpu_temps size=%d; "
121803831d35Sstevel "tables prop_len=%d\n", tblsz, prop_len);
121903831d35Sstevel #endif /* DEBUG */
122003831d35Sstevel
122103831d35Sstevel ddi_prop_free(creg_prop);
122203831d35Sstevel
122303831d35Sstevel /*
122403831d35Sstevel * Read shutdown temp and warning temp properties.
122503831d35Sstevel */
122603831d35Sstevel warning_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
122703831d35Sstevel DDI_PROP_DONTPASS, "warning-temp", PCF8591_WARNING_TEMP);
122803831d35Sstevel
122903831d35Sstevel shutdown_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
123003831d35Sstevel DDI_PROP_DONTPASS, "shutdown-temp", PCF8591_SHUTDOWN_TEMP);
123103831d35Sstevel
123203831d35Sstevel /*
123303831d35Sstevel * Fill up the warning and shutdown temp values in kstat structure.
123403831d35Sstevel */
123503831d35Sstevel unitp->temp_kstats.warning_threshold = warning_temp;
123603831d35Sstevel unitp->temp_kstats.shutdown_threshold = shutdown_temp;
123703831d35Sstevel
123803831d35Sstevel return (DDI_PROP_SUCCESS);
123903831d35Sstevel }
1240