11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
57656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
67656SSherry.Moore@Sun.COM * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel /*
22*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
231708Sstevel * Use is subject to license terms.
241708Sstevel */
251708Sstevel
261708Sstevel /*
271708Sstevel * I2C leaf driver for the PCF8591
281708Sstevel */
291708Sstevel
301708Sstevel #include <sys/param.h>
311708Sstevel #include <sys/types.h>
321708Sstevel #include <sys/signal.h>
331708Sstevel #include <sys/errno.h>
341708Sstevel #include <sys/file.h>
351708Sstevel #include <sys/termio.h>
361708Sstevel #include <sys/termios.h>
371708Sstevel #include <sys/cmn_err.h>
381708Sstevel #include <sys/stream.h>
391708Sstevel #include <sys/strsun.h>
401708Sstevel #include <sys/stropts.h>
411708Sstevel #include <sys/strtty.h>
421708Sstevel #include <sys/debug.h>
431708Sstevel #include <sys/eucioctl.h>
441708Sstevel #include <sys/cred.h>
451708Sstevel #include <sys/uio.h>
461708Sstevel #include <sys/stat.h>
471708Sstevel #include <sys/kmem.h>
481708Sstevel
491708Sstevel #include <sys/ddi.h>
501708Sstevel #include <sys/sunddi.h>
511708Sstevel #include <sys/obpdefs.h>
521708Sstevel #include <sys/conf.h>
531708Sstevel #include <sys/modctl.h>
541708Sstevel #include <sys/stat.h>
551708Sstevel #include <sys/open.h>
561708Sstevel #include <sys/uio.h>
571708Sstevel
581708Sstevel #include <sys/i2c/misc/i2c_svc.h>
591708Sstevel #include <sys/envctrl_gen.h>
601708Sstevel #include <sys/netract_gen.h>
611708Sstevel #include <sys/pcf8591_nct.h>
621708Sstevel
631708Sstevel
641708Sstevel /*
651708Sstevel * CONTROL OF CHIP
661708Sstevel * PCF8591 Temp sensing control register definitions
671708Sstevel *
681708Sstevel * ---------------------------------------------
691708Sstevel * | 0 | AOE | X | X | 0 | AIF | X | X |
701708Sstevel * ---------------------------------------------
711708Sstevel * AOE = Analog out enable.. not used on out implementation
721708Sstevel * 5 & 4 = Analog Input Programming.. see data sheet for bits..
731708Sstevel *
741708Sstevel * AIF = Auto increment flag
751708Sstevel * bits 1 & 0 are for the Chennel number.
761708Sstevel */
771708Sstevel
781708Sstevel
791708Sstevel #define I2CTRANS_DATA 0
801708Sstevel #define I2CRAW_DATA 1
811708Sstevel #define TEMP_TABLE_SIZE 256
821708Sstevel
831708Sstevel #define SHUTDOWN_TEMP_MIN 55
841708Sstevel #define SHUTDOWN_TEMP_MAX 85
851708Sstevel
861708Sstevel #ifdef DEBUG
871708Sstevel #define dbg_print(level, str) cmn_err(level, str);
881708Sstevel #else
891708Sstevel #define dbg_print(level, str) {; }
901708Sstevel #endif
911708Sstevel
921708Sstevel
931708Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
941708Sstevel static uchar_t _cpu_temps[TEMP_TABLE_SIZE + 4]; /* see attach */
951708Sstevel
961708Sstevel static void *pcf8591_soft_statep;
971708Sstevel
981708Sstevel /*
991708Sstevel * cb ops (only need ioctl)
1001708Sstevel */
1011708Sstevel static int pcf8591_open(dev_t *, int, int, cred_t *);
1021708Sstevel static int pcf8591_close(dev_t, int, int, cred_t *);
1031708Sstevel static int pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
1041708Sstevel static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1051708Sstevel
1061708Sstevel static struct cb_ops pcf8591_cbops = {
1071708Sstevel pcf8591_open, /* open */
1081708Sstevel pcf8591_close, /* close */
1091708Sstevel nodev, /* strategy */
1101708Sstevel nodev, /* print */
1111708Sstevel nodev, /* dump */
1121708Sstevel pcf8591_read, /* read */
1131708Sstevel nodev, /* write */
1141708Sstevel pcf8591_ioctl, /* ioctl */
1151708Sstevel nodev, /* devmap */
1161708Sstevel nodev, /* mmap */
1171708Sstevel nodev, /* segmap */
1181708Sstevel nochpoll, /* poll */
1191708Sstevel ddi_prop_op, /* cb_prop_op */
1201708Sstevel NULL, /* streamtab */
1211708Sstevel D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
1221708Sstevel CB_REV, /* rev */
1231708Sstevel nodev, /* int (*cb_aread)() */
1241708Sstevel nodev /* int (*cb_awrite)() */
1251708Sstevel };
1261708Sstevel
1271708Sstevel /*
1281708Sstevel * dev ops
1291708Sstevel */
1301708Sstevel static int pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1311708Sstevel void **result);
1321708Sstevel static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1331708Sstevel static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1341708Sstevel
1351708Sstevel /* kstat routines */
1361708Sstevel static int pcf8591_add_kstats(struct pcf8591_unit *);
1371708Sstevel static void pcf8591_delete_kstats(struct pcf8591_unit *);
1381708Sstevel static int pcf8591_temp_kstat_update(kstat_t *, int);
1391708Sstevel static int pcf8591_read_chip(struct pcf8591_unit *, uint8_t, int);
1401708Sstevel static int pcf8591_read_props(struct pcf8591_unit *unitp);
1411708Sstevel
1421708Sstevel static struct dev_ops pcf8591_ops = {
1431708Sstevel DEVO_REV,
1441708Sstevel 0,
1451708Sstevel pcf8591_info,
1461708Sstevel nulldev,
1471708Sstevel nulldev,
1481708Sstevel pcf8591_attach,
1491708Sstevel pcf8591_detach,
1501708Sstevel nodev,
1511708Sstevel &pcf8591_cbops,
1527656SSherry.Moore@Sun.COM NULL,
1537656SSherry.Moore@Sun.COM NULL,
1547656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1551708Sstevel };
1561708Sstevel
1571708Sstevel extern struct mod_ops mod_driverops;
1581708Sstevel
1591708Sstevel static struct modldrv pcf8591_modldrv = {
1601708Sstevel &mod_driverops, /* type of module - driver */
1617656SSherry.Moore@Sun.COM "Netract pcf8591 (adio)",
1621708Sstevel &pcf8591_ops,
1631708Sstevel };
1641708Sstevel
1651708Sstevel static struct modlinkage pcf8591_modlinkage = {
1661708Sstevel MODREV_1,
1671708Sstevel &pcf8591_modldrv,
1681708Sstevel 0
1691708Sstevel };
1701708Sstevel
1711708Sstevel char _depends_on[] = "misc/i2c_svc";
1721708Sstevel
1731708Sstevel int pcf8591_debug = 0x02;
1741708Sstevel static uint8_t translate_cputemp(uint8_t value);
1751708Sstevel
1761708Sstevel int
_init(void)1771708Sstevel _init(void)
1781708Sstevel {
1791708Sstevel register int error;
1801708Sstevel
1811708Sstevel error = mod_install(&pcf8591_modlinkage);
1821708Sstevel if (error == 0) {
1831708Sstevel (void) ddi_soft_state_init(&pcf8591_soft_statep,
1847656SSherry.Moore@Sun.COM sizeof (struct pcf8591_unit), PCF8591_MAX_DEVS);
1851708Sstevel }
1861708Sstevel
1871708Sstevel return (error);
1881708Sstevel }
1891708Sstevel
1901708Sstevel int
_fini(void)1911708Sstevel _fini(void)
1921708Sstevel {
1931708Sstevel register int error;
1941708Sstevel
1951708Sstevel error = mod_remove(&pcf8591_modlinkage);
1961708Sstevel if (error == 0) {
1971708Sstevel ddi_soft_state_fini(&pcf8591_soft_statep);
1981708Sstevel }
1991708Sstevel
2001708Sstevel return (error);
2011708Sstevel }
2021708Sstevel
2031708Sstevel int
_info(struct modinfo * modinfop)2041708Sstevel _info(struct modinfo *modinfop)
2051708Sstevel {
2061708Sstevel return (mod_info(&pcf8591_modlinkage, modinfop));
2071708Sstevel }
2081708Sstevel
2091708Sstevel /*ARGSUSED*/
2101708Sstevel static int
pcf8591_open(dev_t * devp,int flags,int otyp,cred_t * credp)2111708Sstevel pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp)
2121708Sstevel {
2131708Sstevel int err = 0;
2141708Sstevel struct pcf8591_unit *unitp;
2151708Sstevel minor_t minor = getminor(*devp);
2161708Sstevel
2171708Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
2181708Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
2191708Sstevel
2201708Sstevel if (instance < 0) {
2211708Sstevel return (ENXIO);
2221708Sstevel }
2231708Sstevel
2241708Sstevel unitp = (struct pcf8591_unit *)
2257656SSherry.Moore@Sun.COM ddi_get_soft_state(pcf8591_soft_statep, instance);
2261708Sstevel
2271708Sstevel if (unitp == NULL) {
2281708Sstevel return (ENXIO);
2291708Sstevel }
2301708Sstevel
2311708Sstevel if (otyp != OTYP_CHR) {
2321708Sstevel return (EINVAL);
2331708Sstevel }
2341708Sstevel
2351708Sstevel mutex_enter(&unitp->umutex);
2361708Sstevel
2371708Sstevel if (flags & FEXCL) {
2381708Sstevel if (unitp->pcf8591_oflag[channel] != 0) {
2391708Sstevel err = EBUSY;
2401708Sstevel } else {
2411708Sstevel unitp->pcf8591_oflag[channel] = FEXCL;
2421708Sstevel }
2431708Sstevel } else {
2441708Sstevel if (unitp->pcf8591_oflag[channel] == FEXCL) {
2451708Sstevel err = EBUSY;
2461708Sstevel } else {
2471708Sstevel unitp->pcf8591_oflag[channel] = FOPEN;
2481708Sstevel }
2491708Sstevel }
2501708Sstevel
2511708Sstevel mutex_exit(&unitp->umutex);
2521708Sstevel
2531708Sstevel return (err);
2541708Sstevel }
2551708Sstevel
2561708Sstevel /*ARGSUSED*/
2571708Sstevel static int
pcf8591_close(dev_t devp,int flags,int otyp,cred_t * credp)2581708Sstevel pcf8591_close(dev_t devp, int flags, int otyp, cred_t *credp)
2591708Sstevel {
2601708Sstevel struct pcf8591_unit *unitp;
2611708Sstevel minor_t minor = getminor(devp);
2621708Sstevel
2631708Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
2641708Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
2651708Sstevel
2661708Sstevel #ifdef lint
2671708Sstevel flags = flags;
2681708Sstevel otyp = otyp;
2691708Sstevel #endif
2701708Sstevel
2711708Sstevel if (instance < 0) {
2721708Sstevel return (ENXIO);
2731708Sstevel }
2741708Sstevel
2751708Sstevel unitp = (struct pcf8591_unit *)
2767656SSherry.Moore@Sun.COM ddi_get_soft_state(pcf8591_soft_statep, instance);
2771708Sstevel
2781708Sstevel if (unitp == NULL) {
2791708Sstevel return (ENXIO);
2801708Sstevel }
2811708Sstevel
2821708Sstevel mutex_enter(&unitp->umutex);
2831708Sstevel
2841708Sstevel unitp->pcf8591_oflag[channel] = 0;
2851708Sstevel
2861708Sstevel mutex_exit(&unitp->umutex);
2871708Sstevel
2881708Sstevel return (DDI_SUCCESS);
2891708Sstevel }
2901708Sstevel
2911708Sstevel static int
pcf8591_io(dev_t dev,struct uio * uiop,int rw)2921708Sstevel pcf8591_io(dev_t dev, struct uio *uiop, int rw)
2931708Sstevel {
2941708Sstevel int err = 0;
2951708Sstevel struct pcf8591_unit *unitp;
2961708Sstevel minor_t minor = getminor(dev);
2971708Sstevel
2981708Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
2991708Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
3001708Sstevel
3011708Sstevel int bytes_to_rw;
3021708Sstevel int translate = 0;
3031708Sstevel
3041708Sstevel /*
3051708Sstevel * At this point we don't have a write operation to pcf8591.
3061708Sstevel */
3071708Sstevel if (rw == B_WRITE) {
3081708Sstevel return (EACCES);
3091708Sstevel }
3101708Sstevel
3111708Sstevel if (instance < 0) {
3121708Sstevel return (ENXIO);
3131708Sstevel }
3141708Sstevel
3151708Sstevel unitp = (struct pcf8591_unit *)
3167656SSherry.Moore@Sun.COM ddi_get_soft_state(pcf8591_soft_statep, instance);
3171708Sstevel if (unitp == NULL) {
3181708Sstevel return (ENXIO);
3191708Sstevel }
3201708Sstevel
3211708Sstevel if ((bytes_to_rw = uiop->uio_resid) > PCF8591_TRAN_SIZE) {
3221708Sstevel return (EINVAL);
3231708Sstevel }
3241708Sstevel
3251708Sstevel /*
3261708Sstevel * Need to serialize all read operations, since there is a single
3271708Sstevel * i2c_transfer_t structure allocated for all read and write ops.
3281708Sstevel * We can't share the i2c bus among multiple transactions anyway,
3291708Sstevel * so this does not affect performance.
3301708Sstevel */
3311708Sstevel mutex_enter(&unitp->umutex);
3321708Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
3331708Sstevel if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
3341708Sstevel mutex_exit(&unitp->umutex);
3351708Sstevel
3361708Sstevel return (EINTR);
3371708Sstevel }
3381708Sstevel }
3391708Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
3401708Sstevel mutex_exit(&unitp->umutex);
3411708Sstevel
3421708Sstevel if (bytes_to_rw == 1)
3431708Sstevel translate = 1;
3441708Sstevel /*
3451708Sstevel * Event sequence:
3461708Sstevel * 1. set up the control register write, for now we'll always read
3471708Sstevel * channel 0, which is the only active 8591 port on the Nordica
3481708Sstevel * TODO: We'll need a minor node for each port that is used.
3491708Sstevel * 2. increment read count to read the throw-away byte
3501708Sstevel * 3. start the write/read of control/data registers
3511708Sstevel * 4. throw the first byte away
3521708Sstevel * 5. then return the data
3531708Sstevel */
3541708Sstevel
3551708Sstevel unitp->i2c_tran->i2c_flags = I2C_WR_RD;
3561708Sstevel unitp->i2c_tran->i2c_wlen = 1;
3571708Sstevel unitp->i2c_tran->i2c_wbuf[0] = (unitp->pcf8591_inprog |
3587656SSherry.Moore@Sun.COM channel);
3591708Sstevel /*
3601708Sstevel * read extra byte to throw away the first, (PCF8591 datasheet)
3611708Sstevel */
3621708Sstevel unitp->i2c_tran->i2c_rlen = bytes_to_rw + 1;
3631708Sstevel
3641708Sstevel if (nct_i2c_transfer(unitp->pcf8591_hdl,
3657656SSherry.Moore@Sun.COM unitp->i2c_tran) != I2C_SUCCESS) {
3661708Sstevel err = EIO;
3671708Sstevel } else {
3681708Sstevel /*
3691708Sstevel * Throw away the first byte according to PCF8591 datasheet
3701708Sstevel * If translating, use the second byte.
3711708Sstevel */
3721708Sstevel if (translate) {
3731708Sstevel unitp->i2c_tran->i2c_rbuf[0] =
3747656SSherry.Moore@Sun.COM translate_cputemp(unitp->i2c_tran->i2c_rbuf[1]);
3751708Sstevel } else {
3761708Sstevel unitp->i2c_tran->i2c_rbuf[0] =
3777656SSherry.Moore@Sun.COM unitp->i2c_tran->i2c_rbuf[1];
3781708Sstevel unitp->i2c_tran->i2c_rbuf[1] = 0;
3791708Sstevel }
3801708Sstevel
3811708Sstevel err = uiomove(unitp->i2c_tran->i2c_rbuf,
3827656SSherry.Moore@Sun.COM bytes_to_rw,
3837656SSherry.Moore@Sun.COM UIO_READ,
3847656SSherry.Moore@Sun.COM uiop);
3851708Sstevel }
3861708Sstevel mutex_enter(&unitp->umutex);
3871708Sstevel unitp->pcf8591_flags = 0;
3881708Sstevel cv_signal(&unitp->pcf8591_cv);
3891708Sstevel mutex_exit(&unitp->umutex);
3901708Sstevel
3911708Sstevel return (err);
3921708Sstevel }
3931708Sstevel
3941708Sstevel /*ARGSUSED*/
3951708Sstevel static int
pcf8591_read(dev_t dev,struct uio * uiop,cred_t * cred_p)3961708Sstevel pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
3971708Sstevel {
3981708Sstevel return (pcf8591_io(dev, uiop, B_READ));
3991708Sstevel }
4001708Sstevel
4011708Sstevel static int
call_copyin(caddr_t arg,struct pcf8591_unit * unitp,int mode)4021708Sstevel call_copyin(caddr_t arg, struct pcf8591_unit *unitp, int mode)
4031708Sstevel {
4041708Sstevel uchar_t *wbuf;
4051708Sstevel uchar_t *rbuf;
4061708Sstevel i2c_transfer_t i2ct;
4071708Sstevel i2c_transfer_t *i2ctp = unitp->i2c_tran;
4081708Sstevel
4091708Sstevel
4101708Sstevel if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
4111708Sstevel sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
4127656SSherry.Moore@Sun.COM return (I2C_FAILURE);
4131708Sstevel }
4141708Sstevel
4151708Sstevel /*
4161708Sstevel * Save the read and write buffer pointers in the transfer
4171708Sstevel * structure, otherwise these will get overwritten when we
4181708Sstevel * do a bcopy. Restore once done.
4191708Sstevel */
4201708Sstevel
4211708Sstevel wbuf = i2ctp->i2c_wbuf;
4221708Sstevel rbuf = i2ctp->i2c_rbuf;
4231708Sstevel
4241708Sstevel bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
4251708Sstevel
4261708Sstevel i2ctp->i2c_wbuf = wbuf;
4271708Sstevel i2ctp->i2c_rbuf = rbuf;
4281708Sstevel
4291708Sstevel /*
4301708Sstevel * copyin the read and write buffers to the saved buffers.
4311708Sstevel */
4321708Sstevel
4331708Sstevel if (i2ct.i2c_wlen != 0) {
4341708Sstevel if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
4357656SSherry.Moore@Sun.COM i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
4361708Sstevel return (I2C_FAILURE);
4371708Sstevel }
4381708Sstevel }
4391708Sstevel
4401708Sstevel return (I2C_SUCCESS);
4411708Sstevel }
4421708Sstevel
4431708Sstevel static int
call_copyout(caddr_t arg,struct pcf8591_unit * unitp,int mode)4441708Sstevel call_copyout(caddr_t arg, struct pcf8591_unit *unitp, int mode)
4451708Sstevel {
4461708Sstevel i2c_transfer_t i2ct;
4471708Sstevel i2c_transfer_t *i2ctp = unitp->i2c_tran;
4481708Sstevel uint16_t i2c_actlen;
4491708Sstevel
4501708Sstevel /*
4511708Sstevel * We will copyout the last three fields only, skipping
4521708Sstevel * the remaining ones, before copying the rbuf to the
4531708Sstevel * user buffer.
4541708Sstevel */
4551708Sstevel
4561708Sstevel int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
4577656SSherry.Moore@Sun.COM kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
4581708Sstevel
4591708Sstevel /*
4601708Sstevel * First copyin the user structure to the temporary i2ct,
4611708Sstevel * so that we have the wbuf and rbuf addresses in it.
4621708Sstevel */
4631708Sstevel
4641708Sstevel uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
4651708Sstevel
4661708Sstevel /*
4671708Sstevel * copyout the last three out fields now.
4681708Sstevel */
4691708Sstevel
4701708Sstevel if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
4717656SSherry.Moore@Sun.COM ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
4727656SSherry.Moore@Sun.COM != DDI_SUCCESS) {
4731708Sstevel return (I2C_FAILURE);
4741708Sstevel }
4751708Sstevel
4761708Sstevel /*
4771708Sstevel * In case we have something to write, get the address of the read
4781708Sstevel * buffer.
4791708Sstevel */
4801708Sstevel
4811708Sstevel if (i2ctp->i2c_rlen - i2ctp->i2c_r_resid > 0) {
4821708Sstevel
4831708Sstevel if (ddi_copyin((void *)arg, &i2ct,
4847656SSherry.Moore@Sun.COM sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
4851708Sstevel return (I2C_FAILURE);
4861708Sstevel }
4871708Sstevel
4881708Sstevel /*
4891708Sstevel * copyout the read buffer to the saved user buffer in i2ct.
4901708Sstevel */
4911708Sstevel
4921708Sstevel i2c_actlen = i2ctp->i2c_rlen - i2ctp->i2c_r_resid;
4931708Sstevel if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
4947656SSherry.Moore@Sun.COM i2c_actlen, mode) != DDI_SUCCESS) {
4951708Sstevel return (I2C_FAILURE);
4961708Sstevel }
4971708Sstevel }
4981708Sstevel
4991708Sstevel return (I2C_SUCCESS);
5001708Sstevel }
5011708Sstevel
5021708Sstevel /*
5031708Sstevel * The ioctls will use the same name as the Javelin ioctls. We
5041708Sstevel * will have a very restricted set for MC, and unlike Javelin
5051708Sstevel * will not have a envctrl_chip structure to return values
5061708Sstevel * from the driver. All we will have is a uint8_t value to
5071708Sstevel * get or set values from the driver. Also, unlike the Javelin,
5081708Sstevel * where 'index' is used to specify the input port from where
5091708Sstevel * temperature is collected, here different minor nodes will be
5101708Sstevel * created by the driver for each port, eliminating the need for
5111708Sstevel * 'index' - leaving us with only the value to pass.
5121708Sstevel */
5131708Sstevel
5141708Sstevel /*ARGSUSED*/
5151708Sstevel static int
pcf8591_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)5161708Sstevel pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
5171708Sstevel cred_t *credp, int *rvalp)
5181708Sstevel {
5191708Sstevel int err = 0;
5201708Sstevel struct pcf8591_unit *unitp;
5211708Sstevel minor_t minor = getminor(dev);
5221708Sstevel
5231708Sstevel int instance = PCF8591_MINOR_TO_DEVINST(minor);
5241708Sstevel int channel = PCF8591_MINOR_TO_CHANNEL(minor);
5251708Sstevel
5261708Sstevel unitp = (struct pcf8591_unit *)
5277656SSherry.Moore@Sun.COM ddi_get_soft_state(pcf8591_soft_statep, instance);
5281708Sstevel
5291708Sstevel mutex_enter(&unitp->umutex);
5301708Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
5311708Sstevel if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
5321708Sstevel mutex_exit(&unitp->umutex);
5331708Sstevel
5341708Sstevel return (EINTR);
5351708Sstevel }
5361708Sstevel }
5371708Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
5381708Sstevel mutex_exit(&unitp->umutex);
5391708Sstevel
5401708Sstevel switch (cmd) {
5411708Sstevel
5421708Sstevel case ENVC_IOC_GETTEMP: {
5431708Sstevel /*
5441708Sstevel * Read the status byte from pcf8591 chip. The value will
5451708Sstevel * be already converted to Celcius by translate_cputemp.
5461708Sstevel */
547*11311SSurya.Prakki@Sun.COM (void) pcf8591_read_chip(unitp, channel, 1);
5481708Sstevel if (ddi_copyout(unitp->i2c_tran->i2c_rbuf,
5497656SSherry.Moore@Sun.COM (caddr_t)arg, sizeof (uint8_t), mode) != DDI_SUCCESS) {
5501708Sstevel err = EFAULT;
5511708Sstevel }
5521708Sstevel break;
5531708Sstevel }
5541708Sstevel
5551708Sstevel case ENVC_IOC_GETMODE: {
5561708Sstevel uint8_t curr_mode = unitp->current_mode;
5571708Sstevel
5581708Sstevel if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
5597656SSherry.Moore@Sun.COM sizeof (uint8_t), mode) != DDI_SUCCESS) {
5601708Sstevel err = EFAULT;
5611708Sstevel }
5621708Sstevel break;
5631708Sstevel }
5641708Sstevel
5651708Sstevel case ENVC_IOC_SETMODE: {
5661708Sstevel uint8_t curr_mode;
5671708Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
5687656SSherry.Moore@Sun.COM sizeof (uint8_t), mode) != DDI_SUCCESS) {
5691708Sstevel err = EFAULT;
5701708Sstevel break;
5711708Sstevel }
5721708Sstevel if (curr_mode == ENVCTRL_DIAG_MODE ||
5737656SSherry.Moore@Sun.COM curr_mode == ENVCTRL_NORMAL_MODE) {
5741708Sstevel unitp->current_mode = curr_mode; /* Don't do anything */
5751708Sstevel }
5761708Sstevel break;
5771708Sstevel }
5781708Sstevel
5791708Sstevel /* Testing, may be removed */
5801708Sstevel case I2CDEV_TRAN:
5811708Sstevel if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
5821708Sstevel err = EFAULT;
5831708Sstevel break;
5841708Sstevel }
5851708Sstevel if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
5867656SSherry.Moore@Sun.COM != I2C_SUCCESS) {
5871708Sstevel err = EFAULT;
5881708Sstevel break;
5891708Sstevel }
5901708Sstevel if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
5911708Sstevel err = EFAULT;
5921708Sstevel break;
5931708Sstevel }
5941708Sstevel break;
5951708Sstevel
5961708Sstevel /*
5971708Sstevel * TESTING TRANSLATION from "adc" "table" property
5981708Sstevel * translate thermistor index into temp Celcius
5991708Sstevel */
6001708Sstevel case I2CDEV_GETTEMP: {
6011708Sstevel struct i2c_transfer *tp;
6021708Sstevel if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
6031708Sstevel err = EFAULT;
6041708Sstevel break;
6051708Sstevel }
6061708Sstevel tp = unitp->i2c_tran;
6071708Sstevel if (tp->i2c_rlen != 1) {
6081708Sstevel err = EINVAL;
6091708Sstevel break;
6101708Sstevel }
6111708Sstevel /*
6121708Sstevel * Throw away the first byte according to PCF8591 datasheet,
6131708Sstevel * so read two bytes
6141708Sstevel */
6151708Sstevel tp->i2c_rlen = 2;
6161708Sstevel if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
6177656SSherry.Moore@Sun.COM != I2C_SUCCESS) {
6181708Sstevel err = EFAULT;
6191708Sstevel break;
6201708Sstevel }
6211708Sstevel #ifdef DEBUG
6221708Sstevel if (pcf8591_debug & 0x0010)
6231708Sstevel cmn_err(CE_NOTE,
6247656SSherry.Moore@Sun.COM "pcf8591_ioctl: i2c_rlen=%d; "
6257656SSherry.Moore@Sun.COM "i2c_rbuf[0,1]=0x%x,0x%x\n",
6267656SSherry.Moore@Sun.COM tp->i2c_rlen, tp->i2c_rbuf[0], tp->i2c_rbuf[1]);
6271708Sstevel #endif /* DEBUG */
6281708Sstevel /*
6291708Sstevel * Throw away the first byte according to PCF8591 datasheet
6301708Sstevel */
6311708Sstevel if ((tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]))
6327656SSherry.Moore@Sun.COM == 0) {
6331708Sstevel err = EINVAL;
6341708Sstevel break;
6351708Sstevel }
6361708Sstevel tp->i2c_rbuf[1] = 0;
6371708Sstevel
6381708Sstevel if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
6391708Sstevel err = EFAULT;
6401708Sstevel break;
6411708Sstevel }
6421708Sstevel break;
6431708Sstevel }
6441708Sstevel
6451708Sstevel case I2CDEV_GETTABLES: {
6461708Sstevel break;
6471708Sstevel }
6481708Sstevel default:
6491708Sstevel err = EINVAL;
6501708Sstevel }
6511708Sstevel
6521708Sstevel mutex_enter(&unitp->umutex);
6531708Sstevel unitp->pcf8591_flags = 0;
6541708Sstevel cv_signal(&unitp->pcf8591_cv);
6551708Sstevel mutex_exit(&unitp->umutex);
6561708Sstevel
6571708Sstevel return (err);
6581708Sstevel }
6591708Sstevel
6601708Sstevel static int
pcf8591_do_detach(dev_info_t * dip)6611708Sstevel pcf8591_do_detach(dev_info_t *dip)
6621708Sstevel {
6631708Sstevel register struct pcf8591_unit *unitp;
6641708Sstevel int instance;
6651708Sstevel uint_t attach_flag;
6661708Sstevel
6671708Sstevel instance = ddi_get_instance(dip);
6681708Sstevel unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
6691708Sstevel attach_flag = unitp->attach_flag;
6701708Sstevel
6711708Sstevel if (attach_flag & PCF8591_KSTAT_INIT) {
6721708Sstevel pcf8591_delete_kstats(unitp);
6731708Sstevel }
6741708Sstevel
6751708Sstevel if (attach_flag & PCF8591_LOCK_INIT) {
6761708Sstevel mutex_destroy(&unitp->umutex);
6771708Sstevel cv_destroy(&unitp->pcf8591_cv);
6781708Sstevel }
6791708Sstevel
6801708Sstevel /*
6811708Sstevel * Restore the lengths of the rbuf and wbuf, which was originally
6821708Sstevel * allocated so that the appropriate amount of rbuf and wbuf are
6831708Sstevel * freed.
6841708Sstevel */
6851708Sstevel if (attach_flag & PCF8591_ALLOC_TRANSFER) {
6861708Sstevel unitp->i2c_tran->i2c_wlen = MAX_WLEN;
6871708Sstevel unitp->i2c_tran->i2c_rlen = MAX_RLEN;
6881708Sstevel i2c_transfer_free(unitp->pcf8591_hdl, unitp->i2c_tran);
6891708Sstevel }
6901708Sstevel
6911708Sstevel if (attach_flag & PCF8591_REGISTER_CLIENT) {
6921708Sstevel i2c_client_unregister(unitp->pcf8591_hdl);
6931708Sstevel }
6941708Sstevel
6951708Sstevel if (attach_flag & PCF8591_MINORS_CREATED) {
6961708Sstevel ddi_remove_minor_node(dip, NULL);
6971708Sstevel }
6981708Sstevel
6991708Sstevel /*
7001708Sstevel * Free the memory allocated for the properties.
7011708Sstevel */
7021708Sstevel if (attach_flag & PCF8591_PROPS_READ) {
7031708Sstevel ddi_prop_free(unitp->props.name);
7041708Sstevel if (unitp->props.num_chans_used) {
7051708Sstevel ddi_prop_free(unitp->props.channels_in_use);
7061708Sstevel }
7071708Sstevel
7081708Sstevel if (unitp->props.channels_description) {
7091708Sstevel ddi_prop_free(unitp->props.channels_description);
7101708Sstevel }
7111708Sstevel }
7121708Sstevel
7131708Sstevel if (attach_flag & PCF8591_SOFT_STATE_ALLOC) {
7141708Sstevel ddi_soft_state_free(pcf8591_soft_statep, instance);
7151708Sstevel }
7161708Sstevel
7171708Sstevel return (DDI_SUCCESS);
7181708Sstevel }
7191708Sstevel
7201708Sstevel static int
pcf8591_do_suspend(dev_info_t * dip)7211708Sstevel pcf8591_do_suspend(dev_info_t *dip)
7221708Sstevel {
7231708Sstevel int instance = ddi_get_instance(dip);
7241708Sstevel struct pcf8591_unit *unitp = (struct pcf8591_unit *)
7257656SSherry.Moore@Sun.COM ddi_get_soft_state(pcf8591_soft_statep, instance);
7261708Sstevel
7271708Sstevel if (unitp == NULL) {
7281708Sstevel return (ENXIO);
7291708Sstevel }
7301708Sstevel
7311708Sstevel /*
7321708Sstevel * Set the busy flag so that future transactions block
7331708Sstevel * until resume.
7341708Sstevel */
7351708Sstevel mutex_enter(&unitp->umutex);
7361708Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
7371708Sstevel if (cv_wait_sig(&unitp->pcf8591_cv,
7387656SSherry.Moore@Sun.COM &unitp->umutex) <= 0) {
7391708Sstevel mutex_exit(&unitp->umutex);
7401708Sstevel
7411708Sstevel return (DDI_FAILURE);
7421708Sstevel }
7431708Sstevel }
7441708Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
7451708Sstevel mutex_exit(&unitp->umutex);
7461708Sstevel
7471708Sstevel return (DDI_SUCCESS);
7481708Sstevel }
7491708Sstevel
7501708Sstevel static int
pcf8591_do_resume(dev_info_t * dip)7511708Sstevel pcf8591_do_resume(dev_info_t *dip)
7521708Sstevel {
7531708Sstevel int instance = ddi_get_instance(dip);
7541708Sstevel struct pcf8591_unit *unitp = (struct pcf8591_unit *)
7557656SSherry.Moore@Sun.COM ddi_get_soft_state(pcf8591_soft_statep, instance);
7561708Sstevel if (unitp == NULL) {
7571708Sstevel return (ENXIO);
7581708Sstevel }
7591708Sstevel
7601708Sstevel mutex_enter(&unitp->umutex);
7611708Sstevel unitp->pcf8591_flags = 0;
7621708Sstevel cv_signal(&unitp->pcf8591_cv);
7631708Sstevel mutex_exit(&unitp->umutex);
7641708Sstevel
7651708Sstevel return (DDI_SUCCESS);
7661708Sstevel }
7671708Sstevel
7681708Sstevel static int
pcf8591_do_attach(dev_info_t * dip)7691708Sstevel pcf8591_do_attach(dev_info_t *dip)
7701708Sstevel {
7711708Sstevel register struct pcf8591_unit *unitp;
7721708Sstevel int i, instance;
7731708Sstevel char name[MAXNAMELEN];
7741708Sstevel minor_t minor;
7751708Sstevel
7761708Sstevel instance = ddi_get_instance(dip);
7771708Sstevel
7781708Sstevel if (ddi_soft_state_zalloc(pcf8591_soft_statep, instance) != 0) {
7791708Sstevel return (DDI_FAILURE);
7801708Sstevel }
7811708Sstevel
7821708Sstevel unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
7831708Sstevel
7841708Sstevel if (unitp == NULL) {
7851708Sstevel return (DDI_FAILURE);
7861708Sstevel }
7871708Sstevel
7881708Sstevel unitp->dip = dip;
7891708Sstevel
7901708Sstevel unitp->attach_flag = PCF8591_SOFT_STATE_ALLOC;
7911708Sstevel
7921708Sstevel if (pcf8591_read_props(unitp) != DDI_PROP_SUCCESS) {
793*11311SSurya.Prakki@Sun.COM (void) pcf8591_do_detach(dip);
7941708Sstevel return (DDI_FAILURE);
7951708Sstevel }
7961708Sstevel
7971708Sstevel unitp->attach_flag |= PCF8591_PROPS_READ;
7981708Sstevel
7991708Sstevel /*
8001708Sstevel * Set the current operating mode to NORMAL_MODE.
8011708Sstevel */
8021708Sstevel unitp->current_mode = ENVCTRL_NORMAL_MODE; /* normal mode */
8031708Sstevel
804*11311SSurya.Prakki@Sun.COM (void) snprintf(unitp->pcf8591_name, PCF8591_NAMELEN,
8057656SSherry.Moore@Sun.COM "%s%d", ddi_driver_name(dip), instance);
8061708Sstevel
8071708Sstevel /*
8081708Sstevel * Create a minor node corresponding to channel 0 to 3
8091708Sstevel */
8101708Sstevel for (i = 0; i < PCF8591_MAX_CHANS; i++) {
8111708Sstevel if (i == 0) {
8121708Sstevel (void) sprintf(name, "cputemp");
8131708Sstevel } else {
8141708Sstevel (void) sprintf(name, "%d", i);
8151708Sstevel }
8161708Sstevel minor = PCF8591_MINOR_NUM(instance, i);
8171708Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
8187656SSherry.Moore@Sun.COM PCF8591_NODE_TYPE, NULL) == DDI_FAILURE) {
8191708Sstevel ddi_remove_minor_node(dip, NULL);
820*11311SSurya.Prakki@Sun.COM (void) pcf8591_do_detach(dip);
8211708Sstevel return (DDI_FAILURE);
8221708Sstevel }
8231708Sstevel }
8241708Sstevel
8251708Sstevel unitp->attach_flag |= PCF8591_MINORS_CREATED;
8261708Sstevel
8271708Sstevel if (i2c_client_register(dip, &unitp->pcf8591_hdl)
8287656SSherry.Moore@Sun.COM != I2C_SUCCESS) {
829*11311SSurya.Prakki@Sun.COM (void) pcf8591_do_detach(dip);
8301708Sstevel return (DDI_FAILURE);
8311708Sstevel }
8321708Sstevel
8331708Sstevel unitp->attach_flag |= PCF8591_REGISTER_CLIENT;
8341708Sstevel
8351708Sstevel /*
8361708Sstevel * We allocate a single i2c_transfer_t structure for all
8371708Sstevel * i2c transactions.
8381708Sstevel */
8391708Sstevel if (i2c_transfer_alloc(unitp->pcf8591_hdl, &unitp->i2c_tran,
8407656SSherry.Moore@Sun.COM MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
841*11311SSurya.Prakki@Sun.COM (void) pcf8591_do_detach(dip);
8421708Sstevel return (DDI_FAILURE);
8431708Sstevel }
8441708Sstevel
8451708Sstevel unitp->attach_flag |= PCF8591_ALLOC_TRANSFER;
8461708Sstevel
8471708Sstevel /*
8481708Sstevel * The flags will be set to I2C_WR because for all reads from
8491708Sstevel * the 8591 we need to also write the control byte.
8501708Sstevel */
8511708Sstevel unitp->i2c_tran->i2c_flags = I2C_WR;
8521708Sstevel unitp->i2c_tran->i2c_version = I2C_XFER_REV;
8531708Sstevel
8541708Sstevel
8551708Sstevel /*
8561708Sstevel * Set the analog programming mode to default. Upper nibble
8571708Sstevel * in control byte. Four single ended inputs, output not enabled.
8581708Sstevel */
8591708Sstevel unitp->pcf8591_inprog = PCF8591_4SINGLE | PCF8591_ANALOG_INPUT_EN;
8601708Sstevel
8611708Sstevel /*
8621708Sstevel * Set the open flag for each channel to 0.
8631708Sstevel */
8641708Sstevel for (i = 0; i < PCF8591_MAX_CHANS; i++) {
8651708Sstevel unitp->pcf8591_oflag[i] = 0;
8661708Sstevel }
8671708Sstevel
8681708Sstevel /*
8691708Sstevel * Set the busy flag to 0.
8701708Sstevel */
8711708Sstevel unitp->pcf8591_flags = 0;
8721708Sstevel
8731708Sstevel mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
8741708Sstevel cv_init(&unitp->pcf8591_cv, NULL, CV_DRIVER, NULL);
8751708Sstevel
8761708Sstevel unitp->attach_flag |= PCF8591_LOCK_INIT;
8771708Sstevel
8781708Sstevel if (pcf8591_add_kstats(unitp) != DDI_SUCCESS) {
879*11311SSurya.Prakki@Sun.COM (void) pcf8591_do_detach(dip);
8801708Sstevel return (DDI_FAILURE);
8811708Sstevel }
8821708Sstevel
8831708Sstevel unitp->attach_flag |= PCF8591_KSTAT_INIT;
8841708Sstevel
8851708Sstevel ddi_report_dev(dip);
8861708Sstevel
8871708Sstevel return (DDI_SUCCESS);
8881708Sstevel }
8891708Sstevel
8901708Sstevel /* ARGSUSED */
8911708Sstevel static int
pcf8591_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)8921708Sstevel pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
8931708Sstevel {
8941708Sstevel dev_t dev;
8951708Sstevel int instance;
8961708Sstevel
8971708Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) {
8981708Sstevel dev = (dev_t)arg;
8991708Sstevel instance = PCF8591_MINOR_TO_DEVINST(getminor(dev));
9001708Sstevel *result = (void *)(uintptr_t)instance;
9011708Sstevel return (DDI_SUCCESS);
9021708Sstevel }
9031708Sstevel return (DDI_FAILURE);
9041708Sstevel }
9051708Sstevel
9061708Sstevel static int
pcf8591_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)9071708Sstevel pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
9081708Sstevel {
9091708Sstevel switch (cmd) {
9101708Sstevel case DDI_ATTACH:
9111708Sstevel return (pcf8591_do_attach(dip));
9121708Sstevel case DDI_RESUME:
9131708Sstevel return (pcf8591_do_resume(dip));
9141708Sstevel default:
9151708Sstevel return (DDI_FAILURE);
9161708Sstevel }
9171708Sstevel }
9181708Sstevel
9191708Sstevel static int
pcf8591_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)9201708Sstevel pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9211708Sstevel {
9221708Sstevel switch (cmd) {
9231708Sstevel case DDI_DETACH:
9241708Sstevel return (pcf8591_do_detach(dip));
9251708Sstevel case DDI_SUSPEND:
9261708Sstevel return (pcf8591_do_suspend(dip));
9271708Sstevel default:
9281708Sstevel return (DDI_FAILURE);
9291708Sstevel }
9301708Sstevel }
9311708Sstevel
9321708Sstevel static uint8_t
translate_cputemp(uint8_t value)9331708Sstevel translate_cputemp(uint8_t value)
9341708Sstevel {
9351708Sstevel return (_cpu_temps[value]);
9361708Sstevel }
9371708Sstevel
9381708Sstevel static int
pcf8591_add_kstats(struct pcf8591_unit * unitp)9391708Sstevel pcf8591_add_kstats(struct pcf8591_unit *unitp)
9401708Sstevel {
9411708Sstevel if ((unitp->tempksp = kstat_create(I2C_PCF8591_NAME,
9427656SSherry.Moore@Sun.COM unitp->instance, I2C_KSTAT_CPUTEMP, "misc",
9437656SSherry.Moore@Sun.COM KSTAT_TYPE_RAW, sizeof (unitp->temp_kstats),
9447656SSherry.Moore@Sun.COM KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
9451708Sstevel
9461708Sstevel return (DDI_FAILURE);
9471708Sstevel }
9481708Sstevel
9491708Sstevel /*
9501708Sstevel * The kstat fields are already initialized in the attach routine..
9511708Sstevel */
9521708Sstevel
9531708Sstevel unitp->tempksp->ks_update = pcf8591_temp_kstat_update;
9541708Sstevel unitp->tempksp->ks_private = (void *)unitp;
9551708Sstevel
956*11311SSurya.Prakki@Sun.COM (void) strcpy(unitp->temp_kstats.label,
9577656SSherry.Moore@Sun.COM unitp->props.channels_description[0]);
9581708Sstevel unitp->temp_kstats.type = ENVC_NETRACT_CPU_SENSOR;
9591708Sstevel
9601708Sstevel kstat_install(unitp->tempksp);
9611708Sstevel
9621708Sstevel return (DDI_SUCCESS);
9631708Sstevel }
9641708Sstevel
9651708Sstevel static void
pcf8591_delete_kstats(struct pcf8591_unit * unitp)9661708Sstevel pcf8591_delete_kstats(struct pcf8591_unit *unitp)
9671708Sstevel {
9681708Sstevel kstat_delete(unitp->tempksp);
9691708Sstevel }
9701708Sstevel
9711708Sstevel static int
pcf8591_temp_kstat_update(kstat_t * ksp,int rw)9721708Sstevel pcf8591_temp_kstat_update(kstat_t *ksp, int rw)
9731708Sstevel {
9741708Sstevel struct pcf8591_unit *unitp;
9751708Sstevel char *kstatp;
9761708Sstevel int err = 0;
9771708Sstevel int channel = 0;
9781708Sstevel int warn_temp = 0;
9791708Sstevel int shutdown_temp = 0;
9801708Sstevel
9811708Sstevel unitp = (struct pcf8591_unit *)ksp->ks_private;
9821708Sstevel
9831708Sstevel mutex_enter(&unitp->umutex);
9841708Sstevel while (unitp->pcf8591_flags == PCF8591_BUSY) {
9851708Sstevel if (cv_wait_sig(&unitp->pcf8591_cv,
9867656SSherry.Moore@Sun.COM &unitp->umutex) <= 0) {
9871708Sstevel mutex_exit(&unitp->umutex);
9881708Sstevel
9891708Sstevel return (EINTR);
9901708Sstevel }
9911708Sstevel }
9921708Sstevel
9931708Sstevel unitp->pcf8591_flags = PCF8591_BUSY;
9941708Sstevel mutex_exit(&unitp->umutex);
9951708Sstevel
9961708Sstevel kstatp = (char *)ksp->ks_data;
9971708Sstevel
9981708Sstevel if (rw == KSTAT_WRITE) {
9991708Sstevel
10001708Sstevel /* check for the size of buffer */
10011708Sstevel if (ksp->ks_data_size != sizeof (unitp->temp_kstats)) {
10021708Sstevel err = EIO;
10031708Sstevel goto bail;
10041708Sstevel }
10051708Sstevel
10061708Sstevel warn_temp = ((envctrl_temp_t *)kstatp)->warning_threshold;
10071708Sstevel shutdown_temp = ((envctrl_temp_t *)kstatp)->shutdown_threshold;
10081708Sstevel
10091708Sstevel if (shutdown_temp < SHUTDOWN_TEMP_MIN || shutdown_temp >
10107656SSherry.Moore@Sun.COM SHUTDOWN_TEMP_MAX) {
10111708Sstevel err = EIO;
10121708Sstevel goto bail;
10131708Sstevel }
10141708Sstevel
10151708Sstevel if (warn_temp < 0 || shutdown_temp <= warn_temp) {
10161708Sstevel err = EIO;
10171708Sstevel goto bail;
10181708Sstevel }
10191708Sstevel
10201708Sstevel /* write into kstat fields */
10211708Sstevel unitp->temp_kstats.warning_threshold = warn_temp;
10221708Sstevel unitp->temp_kstats.shutdown_threshold = shutdown_temp;
10231708Sstevel
10241708Sstevel } else {
1025*11311SSurya.Prakki@Sun.COM (void) pcf8591_read_chip(unitp, channel, 1);
10261708Sstevel unitp->temp_kstats.value =
10277656SSherry.Moore@Sun.COM unitp->i2c_tran->i2c_rbuf[0];
10281708Sstevel bcopy((caddr_t)&unitp->temp_kstats, kstatp,
10297656SSherry.Moore@Sun.COM sizeof (unitp->temp_kstats));
10301708Sstevel }
10311708Sstevel
10321708Sstevel bail:
10331708Sstevel
10341708Sstevel mutex_enter(&unitp->umutex);
10351708Sstevel unitp->pcf8591_flags = 0;
10361708Sstevel cv_signal(&unitp->pcf8591_cv);
10371708Sstevel mutex_exit(&unitp->umutex);
10381708Sstevel
10391708Sstevel return (err);
10401708Sstevel }
10411708Sstevel
10421708Sstevel static int
pcf8591_read_chip(struct pcf8591_unit * unitp,uint8_t channel,int size)10431708Sstevel pcf8591_read_chip(struct pcf8591_unit *unitp, uint8_t channel,
10441708Sstevel int size)
10451708Sstevel {
10461708Sstevel int retval = I2C_SUCCESS;
10471708Sstevel
10481708Sstevel /*
10491708Sstevel * We need to read an extra byte, since as per specification
10501708Sstevel * the first byte read should be discarded.
10511708Sstevel */
10521708Sstevel i2c_transfer_t *tp = unitp->i2c_tran;
10531708Sstevel tp->i2c_flags = I2C_WR_RD;
10541708Sstevel tp->i2c_rlen = size+1;
10551708Sstevel tp->i2c_wlen = 1;
10561708Sstevel tp->i2c_wbuf[0] = (unitp->pcf8591_inprog |
10577656SSherry.Moore@Sun.COM channel);
10581708Sstevel
10591708Sstevel retval = nct_i2c_transfer(unitp->pcf8591_hdl, tp);
10601708Sstevel if (retval == I2C_SUCCESS) {
10611708Sstevel tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]);
10621708Sstevel }
10631708Sstevel
10641708Sstevel if (tp->i2c_rbuf[0] == 0) {
10651708Sstevel retval = I2C_FAILURE;
10661708Sstevel }
10671708Sstevel
10681708Sstevel return (retval);
10691708Sstevel }
10701708Sstevel
10711708Sstevel /*
10721708Sstevel * Reads the properties of the pcf8591 device.
10731708Sstevel */
10741708Sstevel static int
pcf8591_read_props(struct pcf8591_unit * unitp)10751708Sstevel pcf8591_read_props(struct pcf8591_unit *unitp)
10761708Sstevel {
10771708Sstevel dev_info_t *dip = unitp->dip;
10781708Sstevel int i, retval = 0, prop_len;
10791708Sstevel int instance = ddi_get_instance(dip);
10801708Sstevel int warning_temp, shutdown_temp;
10811708Sstevel uint32_t *prop_value = NULL;
10821708Sstevel uchar_t *creg_prop;
10831708Sstevel char *function;
10841708Sstevel uint_t tblsz;
10851708Sstevel
10861708Sstevel #ifdef lint
10871708Sstevel instance = instance;
10881708Sstevel #endif
10891708Sstevel /*
10901708Sstevel * Check for the pcf8591_function property, and make sure it's
10911708Sstevel * cputemp.
10921708Sstevel */
10931708Sstevel if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
10947656SSherry.Moore@Sun.COM "pcf8591_function", &function) != DDI_SUCCESS) {
10951708Sstevel dbg_print(CE_WARN, "Couldn't find pcf8591_function property");
10961708Sstevel
10971708Sstevel return (DDI_FAILURE);
10981708Sstevel }
10991708Sstevel
11001708Sstevel if (strcmp(function, "cputemp") != 0) {
11011708Sstevel dbg_print(CE_WARN, "pcf8591_function is not cputemp");
11021708Sstevel ddi_prop_free(function);
11031708Sstevel
11041708Sstevel return (DDI_FAILURE);
11051708Sstevel }
11061708Sstevel
11071708Sstevel ddi_prop_free(function);
11081708Sstevel
11091708Sstevel retval = ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11107656SSherry.Moore@Sun.COM "name", &unitp->props.name);
11111708Sstevel if (retval != DDI_PROP_SUCCESS) {
11121708Sstevel
11131708Sstevel return (retval);
11141708Sstevel }
11151708Sstevel #ifdef DEBUG
11161708Sstevel else if (pcf8591_debug & 0x02)
11171708Sstevel cmn_err(CE_NOTE,
11187656SSherry.Moore@Sun.COM "pcf8591_read_props:ddi_prop_lookup_string(%s): \
11191708Sstevel found %s ", "name", unitp->props.name);
11201708Sstevel #endif /* DEBUG */
11211708Sstevel
11221708Sstevel retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
11237656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
11247656SSherry.Moore@Sun.COM "reg", (caddr_t)&prop_value, &prop_len);
11251708Sstevel if (retval == DDI_PROP_SUCCESS) {
11261708Sstevel unitp->props.i2c_bus = (uint16_t)prop_value[0];
11271708Sstevel unitp->props.slave_address = (uint16_t)prop_value[1];
11281708Sstevel kmem_free(prop_value, prop_len);
11291708Sstevel #ifdef DEBUG
11301708Sstevel if (pcf8591_debug & 0x02)
11311708Sstevel cmn_err(CE_NOTE,
11327656SSherry.Moore@Sun.COM "pcf8591:ddi_getlongprop(%s) returns %d,"
11337656SSherry.Moore@Sun.COM " i2c_bus,slave=0x%x,0x%x",
11347656SSherry.Moore@Sun.COM "reg", retval, unitp->props.i2c_bus,
11357656SSherry.Moore@Sun.COM unitp->props.slave_address);
11361708Sstevel #endif /* DEBUG */
11371708Sstevel } else {
11381708Sstevel unitp->props.i2c_bus = (uint16_t)-1;
11391708Sstevel unitp->props.slave_address = (uint16_t)-1;
11401708Sstevel #ifdef DEBUG
11411708Sstevel cmn_err(CE_WARN,
11427656SSherry.Moore@Sun.COM "pcf8591_read_props:ddi_getlongprop(%s) returns %d,"
11437656SSherry.Moore@Sun.COM " default it to 0x%x:0x%X",
11447656SSherry.Moore@Sun.COM "reg", retval, unitp->props.i2c_bus,
11457656SSherry.Moore@Sun.COM unitp->props.slave_address);
11461708Sstevel #endif /* DEBUG */
11471708Sstevel }
1148*11311SSurya.Prakki@Sun.COM (void) ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11497656SSherry.Moore@Sun.COM "channels-in-use", &prop_len);
11501708Sstevel retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
11517656SSherry.Moore@Sun.COM dip, DDI_PROP_DONTPASS,
11527656SSherry.Moore@Sun.COM "channels-in-use",
11537656SSherry.Moore@Sun.COM (uchar_t **)&unitp->props.channels_in_use,
11547656SSherry.Moore@Sun.COM &unitp->props.num_chans_used);
11551708Sstevel if (retval == DDI_PROP_SUCCESS) {
11561708Sstevel unitp->props.num_chans_used /= sizeof (pcf8591_channel_t);
11571708Sstevel } else {
11581708Sstevel unitp->props.num_chans_used = 0;
11591708Sstevel }
11601708Sstevel
11611708Sstevel #ifdef DEBUG
11621708Sstevel if (pcf8591_debug & 0x0002)
11631708Sstevel cmn_err(CE_NOTE,
11647656SSherry.Moore@Sun.COM "pcf8591_read_props:ddi_prop_lookup_byte_array(%s)"
11657656SSherry.Moore@Sun.COM "returns %d\n"
11667656SSherry.Moore@Sun.COM "\t\tlength=%d, #elements=%d",
11677656SSherry.Moore@Sun.COM "channels-in-use", retval,
11687656SSherry.Moore@Sun.COM prop_len, unitp->props.num_chans_used);
11691708Sstevel #endif /* DEBUG */
11701708Sstevel
11711708Sstevel retval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
11727656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "channels-description",
11737656SSherry.Moore@Sun.COM (char ***)&unitp->props.channels_description,
11747656SSherry.Moore@Sun.COM (uint_t *)&prop_len);
11751708Sstevel
11761708Sstevel if (retval != DDI_PROP_SUCCESS) {
11771708Sstevel prop_len = 0;
11781708Sstevel unitp->props.channels_description = NULL;
11791708Sstevel }
11801708Sstevel
11811708Sstevel #ifdef DEBUG
11821708Sstevel if (pcf8591_debug & 0x0002) {
11831708Sstevel cmn_err(CE_NOTE,
11847656SSherry.Moore@Sun.COM "pcf8591_read_props:ddi_prop_lookup_string_array(%s)"
11857656SSherry.Moore@Sun.COM "returns %d, length=%d",
11867656SSherry.Moore@Sun.COM "channels-description", retval, prop_len);
11871708Sstevel for (i = 0; i < prop_len; ++i) {
11881708Sstevel cmn_err(CE_NOTE, "channels-description[%d]=<%s>",
11897656SSherry.Moore@Sun.COM i, unitp->props.channels_description[i]);
11901708Sstevel }
11911708Sstevel }
11921708Sstevel #endif /* DEBUG */
11931708Sstevel
11941708Sstevel /*
11951708Sstevel * The following code was borrowed from envctrltwo.c
11961708Sstevel * I haven't yet investigated why the copy target is index + 2
11971708Sstevel */
11981708Sstevel retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
11997656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "tables", &creg_prop, (uint_t *)&prop_len);
12001708Sstevel
12011708Sstevel if (retval != DDI_PROP_SUCCESS) {
12021708Sstevel #ifdef DEBUG
12031708Sstevel cmn_err(CE_WARN, "%s%d: Unable to read pcf8591 tables property",
12047656SSherry.Moore@Sun.COM ddi_get_name(dip), instance);
12051708Sstevel #endif /* DEBUG */
12061708Sstevel
12071708Sstevel return (DDI_NOT_WELL_FORMED);
12081708Sstevel }
12091708Sstevel
12101708Sstevel tblsz = (sizeof (_cpu_temps) / sizeof (uchar_t));
12111708Sstevel if (prop_len <= tblsz) {
12121708Sstevel for (i = 0; i < prop_len; i++) {
12131708Sstevel _cpu_temps[i] = creg_prop[i];
12141708Sstevel }
12151708Sstevel }
12161708Sstevel #ifdef DEBUG
12171708Sstevel if (pcf8591_debug & 0x0002)
12181708Sstevel cmn_err(CE_NOTE, "pcf8591_read_props: _cpu_temps size=%d; "
12197656SSherry.Moore@Sun.COM "tables prop_len=%d\n", tblsz, prop_len);
12201708Sstevel #endif /* DEBUG */
12211708Sstevel
12221708Sstevel ddi_prop_free(creg_prop);
12231708Sstevel
12241708Sstevel /*
12251708Sstevel * Read shutdown temp and warning temp properties.
12261708Sstevel */
12271708Sstevel warning_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
12287656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "warning-temp", PCF8591_WARNING_TEMP);
12291708Sstevel
12301708Sstevel shutdown_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
12317656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "shutdown-temp", PCF8591_SHUTDOWN_TEMP);
12321708Sstevel
12331708Sstevel /*
12341708Sstevel * Fill up the warning and shutdown temp values in kstat structure.
12351708Sstevel */
12361708Sstevel unitp->temp_kstats.warning_threshold = warning_temp;
12371708Sstevel unitp->temp_kstats.shutdown_threshold = shutdown_temp;
12381708Sstevel
12391708Sstevel return (DDI_PROP_SUCCESS);
12401708Sstevel }
1241