Subject: kern/32463: patch to support h/w rnd# generator, smbus1.0 in AMD-8111 and support for ADT7463
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <anil_public@yahoo.com>
List: netbsd-bugs
Date: 01/06/2006 03:40:00
>Number: 32463
>Category: kern
>Synopsis: patch to support h/w rnd# generator, smbus1.0 in AMD-8111 and support for ADT7463
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Fri Jan 06 03:40:00 +0000 2006
>Originator: Anil Gopinath
>Release: current
>Organization:
>Environment:
NetBSD r2t7 3.99.15 NetBSD 3.99.15 (GENERIC) #2: Thu Jan 5 19:00:33 PST 2006
>Description:
The following patch provides support for:
- hardware random # generator found in AMD-8111
- smbus1.0 host controller found in AMD-8111
- ADT 7463 remote thermal controller and voltage monitor
This functionality has been tested on an TYAN S2881 board.
1. Modifications to existing files:
sys/dev/pci/amdpm.c
sys/dev/pci/amdpmreg.h
sys/dev/pci/files.pci
sys/dev/i2c/files.i2c
sys/dev/DEVNAMES
2. New files added:
sys/dev/pci/amdpm_smbus.c
sys/dev/pci/amdpm_smbusreg.h
sys/dev/i2c/adt7463.c
sys/dev/i2c/adt7463reg.h
3. How to use:
Add the following to your config file (eg. GENERIC)
# hardware random # generator found in AMD-8111
options AMDPM_MAXCALLOUTS=1 # max callouts for rnd generator
# set AMDPM_MAXCALLOUTS=2 if
# higher rate of random # generation
# is required
amdpm* at pci? dev? function?
# enable smbus 1.0 for AMD-8111
iic* at amdpm?
# ADT7463 remote thermal controller and voltage monitor
adt7463c* at iic? addr 0x2D # modify address to suit your board
# 0x2D used in TYAN S2281
# possible addr 0x2C 0x2D 0x2E
>How-To-Repeat:
How to test:
1. check boot messages (dmesg) to see if AMD-8111 hardware random # and ADT7463 were detected
iic0 at amdpm0: I2C bus
adt7463c0 at iic0 addr 0x2d
amdpm0 at pci0 dev 7 function 3
amdpm0: random number generator enabled (apprx. 57ms)
2. Run envstat from the command line. The new sensors should show up in the list.
>Fix:
Index: amdpm.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/amdpm.c,v
retrieving revision 1.9
diff -r1.9 amdpm.c
38a39,44
> /* The following functionality was added by Anil Gopinath (anil_public@yahoo.com)
> * - support for AMD-8111
> * - support for multiple callbacks. This is useful if you have
> * applications that read from /dev/random very often.
> */
>
49a56
> #include <sys/malloc.h>
54c61
<
---
> #include <dev/i2c/i2cvar.h>
55a63
> #include <dev/pci/amdpm_smbusreg.h>
57,72c65,68
< struct amdpm_softc {
< struct device sc_dev;
<
< pci_chipset_tag_t sc_pc;
< pcitag_t sc_tag;
<
< bus_space_tag_t sc_iot;
< bus_space_handle_t sc_ioh; /* PMxx space */
<
< struct callout sc_rnd_ch;
< rndsource_element_t sc_rnd_source;
< #ifdef AMDPM_RND_COUNTERS
< struct evcnt sc_rnd_hits;
< struct evcnt sc_rnd_miss;
< struct evcnt sc_rnd_data[256];
< #endif
---
> struct amdpm_callout_handle {
> int id;
> struct callout rnd_ch;
> struct amdpm_softc *sc;
87,90c83,90
<
< if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
< PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC)
< return (1);
---
>
> /* test for both AMD_PBC768 and AMD-8111 */
> if ( (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD) &&
> ( ( (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC8111_ACPI) &&
> (pa->pa_function == AMDPM_8111_FUNCTION) ) ||
> ( PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC) ) )
> return (1);
>
114,115c114,123
<
< reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG);
---
>
> /* enable random # generation and pm i/o space for AMD-8111 */
> if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC8111_ACPI) {
>
> reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG);
> pci_conf_write(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG, reg|
> AMDPM_RNGEN|AMDPM_PMIOEN);
> }
>
> reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG);
117,119c125,127
< aprint_error("%s: PMxx space isn't enabled\n",
< sc->sc_dev.dv_xname);
< return;
---
> aprint_error("%s: PMxx space isn't enabled\n",
> sc->sc_dev.dv_xname);
> return;
120a129,130
>
>
130,131c140
< pci_conf_write(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG, reg | AMDPM_RNGEN);
< reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG);
---
>
147c156
< callout_init(&sc->sc_rnd_ch);
---
>
173c182,191
< amdpm_rnd_callout(sc);
---
>
> /* initialize all the callouts */
> for(i = 0; i < AMDPM_MAXCALLOUTS; i++) {
> struct amdpm_callout_handle* handle = (struct amdpm_callout_handle*)
> malloc(sizeof(struct amdpm_callout_handle), M_DEVBUF, M_WAITOK);
> handle->sc = sc;
> handle->id = i + 1; /* start at 1 */
> callout_init(&(handle->rnd_ch));
> amdpm_rnd_callout((void*)handle);
> }
175a194,198
>
> /* try to attach to devices on the smbus */
> if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC8111_ACPI) {
> amdpm_smbus_attach(sc);
> }
184c207,208
< struct amdpm_softc *sc = v;
---
> struct amdpm_callout_handle* handle = (struct amdpm_callout_handle*)v;
> struct amdpm_softc *sc = handle->sc;
185a210
>
189,194c214,222
<
< if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGSTAT) &
< AMDPM_RNGDONE) != 0) {
< reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA);
< rnd_add_data(&sc->sc_rnd_source, ®,
< sizeof(reg), sizeof(reg) * NBBY);
---
>
> /* if random # collection is disabled, then just return. */
> if ( RND_ENABLED(&sc->sc_rnd_source) ) {
>
> if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGSTAT) &
> AMDPM_RNGDONE) != 0) {
> reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA);
> rnd_add_data(&sc->sc_rnd_source, ®,
> sizeof(reg), sizeof(reg) * NBBY);
196,202c224,238
< AMDPM_RNDCNT_INCR(&sc->sc_rnd_hits);
< for (i = 0; i < sizeof(reg); i++, reg >>= NBBY)
< AMDPM_RNDCNT_INCR(&sc->sc_rnd_data[reg & 0xff]);
< #endif
< } else
< AMDPM_RNDCNT_INCR(&sc->sc_rnd_miss);
< callout_reset(&sc->sc_rnd_ch, 1, amdpm_rnd_callout, sc);
---
> AMDPM_RNDCNT_INCR(&sc->sc_rnd_hits);
> for (i = 0; i < sizeof(reg); i++, reg >>= NBBY)
> AMDPM_RNDCNT_INCR(&sc->sc_rnd_data[reg & 0xff]);
> #endif
> /* dont delay the last callout */
> if ( (handle->id != AMDPM_MAXCALLOUTS) && (AMDPM_MAXCALLOUTS > 1) )
> delay(AMDPM_8111_RND_GENTIME);
>
> } else
> AMDPM_RNDCNT_INCR(&sc->sc_rnd_miss);
>
> }
>
> callout_reset(&(handle->rnd_ch), 1, amdpm_rnd_callout, v);
>
203a240
>
Index: amdpmreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/amdpmreg.h,v
retrieving revision 1.1
diff -r1.1 amdpmreg.h
38a39,41
> #ifndef _DEV_PCI_AMDPMREG_H_
> #define _DEV_PCI_AMDPMREG_H_
>
58a62,90
>
> #ifndef AMDPM_MAXCALLOUTS
> #define AMDPM_MAXCALLOUTS 1 /* set the default to 1 */
> #endif
>
> #define AMDPM_8111_FUNCTION 3
> #define AMDPM_8111_RND_GENTIME 128 /* time(usec) taken to generate a random # */
>
> struct amdpm_softc {
> struct device sc_dev;
>
> pci_chipset_tag_t sc_pc;
> pcitag_t sc_tag;
>
> bus_space_tag_t sc_iot;
> bus_space_handle_t sc_ioh; /* PMxx space */
>
> rndsource_element_t sc_rnd_source;
> i2c_addr_t sc_smbus_slaveaddr; /* address of slave thats connected to smbus */
>
> struct i2c_controller sc_i2c; /* i2c controller info */
> #ifdef AMDPM_RND_COUNTERS
> struct evcnt sc_rnd_hits;
> struct evcnt sc_rnd_miss;
> struct evcnt sc_rnd_data[256];
> #endif
> };
>
> #endif /* _DEV_PCI_AMDPMREG_H_ */
Index: files.pci
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/files.pci,v
retrieving revision 1.241
diff -r1.241 files.pci
644,648c644,652
< # AMD 768MPX power management controller
< defflag opt_amdpm.h AMDPM_RND_COUNTERS
< device amdpm {}
< attach amdpm at pci
< file dev/pci/amdpm.c amdpm
---
> # AMD 768MPX power management controller / AMD 8111 HyperTransport I/O
> defflag opt_amdpm.h AMDPM_RND_COUNTERS
> defparam opt_amdpm.h AMDPM_MAXCALLOUTS
> define amdpm {}
> device amdpm : i2cbus, amdpm
> attach amdpm at pci
> file dev/pci/amdpm.c amdpm
> file dev/pci/amdpm_smbus.c amdpm
>
Index: files.i2c
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/files.i2c,v
retrieving revision 1.6
diff -r1.6 files.i2c
60a61,66
> # Analog Devices ADT 7463 thermal monitor / fan controller
> define adt7463c {}
> device adt7463c: sysmon_envsys
> attach adt7463c at iic
> file dev/i2c/adt7463c.c adt7463c
>
Index: DEVNAMES
===================================================================
RCS file: /cvsroot/src/sys/dev/DEVNAMES,v
retrieving revision 1.191
diff -r1.191 DEVNAMES
38a39
> adt7463c MI
=========
New files:
1. sys/dev/pci/amdpm_smbus.c
/*
* Copyright (c) 2005 Anil Gopinath (anil_public@yahoo.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* driver for SMBUS 1.0 host controller found in the
* AMD-8111 HyperTransport I/O Hub
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/rnd.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>
#include <dev/pci/amdpmreg.h>
#include <dev/pci/amdpm_smbusreg.h>
static int amdpm_smbus_acquire_bus(void *cookie, int flags);
static void amdpm_smbus_release_bus(void *cookie, int flags);
static int amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
const void *cmd, size_t cmdlen, void *vbuf,
size_t buflen, int flags);
static int amdpm_smbus_check_done(struct amdpm_softc *sc);
static void amdpm_smbus_clear_gsr(struct amdpm_softc *sc);
static u_int16_t amdpm_smbus_get_gsr(struct amdpm_softc *sc);
static int amdpm_smbus_send_1(struct amdpm_softc *sc, u_int8_t val);
static int amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t data);
static int amdpm_smbus_receive_1(struct amdpm_softc *sc);
static int amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd);
void
amdpm_smbus_attach(struct amdpm_softc *sc)
{
struct i2cbus_attach_args iba;
// register with iic
sc->sc_i2c.ic_cookie = sc;
sc->sc_i2c.ic_acquire_bus = amdpm_smbus_acquire_bus;
sc->sc_i2c.ic_release_bus = amdpm_smbus_release_bus;
sc->sc_i2c.ic_send_start = NULL;
sc->sc_i2c.ic_send_stop = NULL;
sc->sc_i2c.ic_initiate_xfer = NULL;
sc->sc_i2c.ic_read_byte = NULL;
sc->sc_i2c.ic_write_byte = NULL;
sc->sc_i2c.ic_exec = amdpm_smbus_exec;
iba.iba_name = "iic";
iba.iba_tag = &sc->sc_i2c;
(void) config_found(&sc->sc_dev, &iba, iicbus_print);
}
static int
amdpm_smbus_acquire_bus(void *cookie, int flags)
{
return (0);
}
static void
amdpm_smbus_release_bus(void *cookie, int flags)
{
}
static int
amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
size_t cmdlen, void *vbuf, size_t buflen, int flags)
{
struct amdpm_softc *sc = (struct amdpm_softc *) cookie;
sc->sc_smbus_slaveaddr = addr;
if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) {
return (amdpm_smbus_receive_1(sc));
}
if ( (I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
return (amdpm_smbus_read_1(sc, *(const uint8_t*)cmd));
}
if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) {
return (amdpm_smbus_send_1(sc, *(uint8_t*)vbuf));
}
if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) {
return (amdpm_smbus_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf));
}
return (-1);
}
static int
amdpm_smbus_check_done(struct amdpm_softc *sc)
{
int i = 0;
for (i = 0; i < 1000; i++) {
/* check gsr and wait till cycle is done */
u_int16_t data = amdpm_smbus_get_gsr(sc);
if (data & AMDPM_8111_GSR_CYCLE_DONE) {
return (0);
}
delay(1);
}
return (-1);
}
static void
amdpm_smbus_clear_gsr(struct amdpm_softc *sc)
{
/* clear register */
u_int16_t data = 0xFFFF;
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_STAT, data);
}
static u_int16_t
amdpm_smbus_get_gsr(struct amdpm_softc *sc)
{
return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_STAT));
}
static int
amdpm_smbus_send_1(struct amdpm_softc *sc, u_int8_t val)
{
/* first clear gsr */
amdpm_smbus_clear_gsr(sc);
/* write smbus slave address to register */
u_int16_t data = 0;
data = sc->sc_smbus_slaveaddr;
data <<= 1;
data |= AMDPM_8111_SMBUS_SEND;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
data = val;
/* store data */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA, data);
/* host start */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL,
AMDPM_8111_SMBUS_GSR_SB);
return(amdpm_smbus_check_done(sc));
}
static int
amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t val)
{
/* first clear gsr */
amdpm_smbus_clear_gsr(sc);
u_int16_t data = 0;
data = sc->sc_smbus_slaveaddr;
data <<= 1;
data |= AMDPM_8111_SMBUS_WRITE;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
data = val;
/* store cmd */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTCMD, cmd);
/* store data */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA, data);
/* host start */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_WB);
return (amdpm_smbus_check_done(sc));
}
static int
amdpm_smbus_receive_1(struct amdpm_softc *sc)
{
/* first clear gsr */
amdpm_smbus_clear_gsr(sc);
/* write smbus slave address to register */
u_int16_t data = 0;
data = sc->sc_smbus_slaveaddr;
data <<= 1;
data |= AMDPM_8111_SMBUS_RX;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
/* start smbus cycle */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_RXB);
/* check for errors */
if (amdpm_smbus_check_done(sc) < 0)
return (-1);
/* read data */
data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA);
u_int8_t ret = (u_int8_t)(data & 0x00FF);
return (ret);
}
static int
amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd)
{
/* first clear gsr */
amdpm_smbus_clear_gsr(sc);
/* write smbus slave address to register */
u_int16_t data = 0;
data = sc->sc_smbus_slaveaddr;
data <<= 1;
data |= AMDPM_8111_SMBUS_READ;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
/* store cmd */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTCMD, cmd);
/* host start */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_RB);
/* check for errors */
if (amdpm_smbus_check_done(sc) < 0)
return (-1);
/* store data */
data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA);
u_int8_t ret = (u_int8_t)(data & 0x00FF);
return (ret);
}
================
2. sys/dev/pci/amdpm_smbusreg.h
/*
* Copyright (c) 2005 Anil Gopinath (anil_public@yahoo.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* driver for SMBUS 1.0 host controller found in the
* AMD-8111 HyperTransport I/O Hub
*/
#ifndef _DEV_PCI_AMDPMSMBUSREG_H_
#define _DEV_PCI_AMDPMSMBUSREG_H_
#define AMDPM_8111_SMBUS_STAT 0xE0 /* SMBus 1.x global status register */
#define AMDPM_8111_SMBUS_CTRL 0xE2 /* SMBus 1.x global control register */
#define AMDPM_8111_SMBUS_HOSTADDR 0xE4 /* SMBus 1.x Host address register */
#define AMDPM_8111_SMBUS_HOSTDATA 0xE6 /* SMBus 1.x Host data register */
#define AMDPM_8111_SMBUS_HOSTCMD 0xE8 /* SMBus 1.x Host command field register */
#define AMDPM_8111_SMBUS_GSR_SB 0x0009 /* GSR contents to send a byte */
#define AMDPM_8111_SMBUS_GSR_RXB 0x0009 /* GSR contents to receive a byte */
#define AMDPM_8111_SMBUS_GSR_RB 0x000A /* GSR contents to read a byte */
#define AMDPM_8111_SMBUS_GSR_WB 0x000A /* GSR contents to write a byte */
#define AMDPM_8111_GSR_CYCLE_DONE 0x0010 /* indicates cycle done successfuly */
#define AMDPM_8111_SMBUS_READ 0x0001 /* smbus read cycle indicator */
#define AMDPM_8111_SMBUS_RX 0x0001 /* smbus receive cycle indicator */
#define AMDPM_8111_SMBUS_WRITE 0x0000 /* smbus write cycle indicator */
#define AMDPM_8111_SMBUS_SEND 0x0000 /* smbus send cycle indicator */
void amdpm_smbus_attach(struct amdpm_softc *sc);
#endif
========
3. sys/dev/i2c/adt7463.c
/*
* Copyright (c) 2005 Anil Gopinath (anil_public@yahoo.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Analog devices AD7463 remote thermal controller and voltage monitor
* Data sheet at:
* http://www.analog.com/UploadedFiles/Data_Sheets/272624927ADT7463_c.pdf
*/
/* Fan speed control added by Hanns Hartman */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <dev/sysmon/sysmonvar.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/adt7463creg.h>
int adt7463c_gtredata __P((struct sysmon_envsys *, struct envsys_tre_data *));
static int adt7463c_send_1(struct adt7463c_softc *sc, u_int8_t val);
static int adt7463c_receive_1(struct adt7463c_softc *sc);
static int adt7463c_write_1(struct adt7463c_softc *sc, u_int8_t cmd, u_int8_t val);
static void adt7463c_setup_volt(struct adt7463c_softc *sc, int start, int tot);
static void adt7463c_setup_temp(struct adt7463c_softc *sc, int start, int tot);
static void adt7463c_setup_fan(struct adt7463c_softc *sc, int start, int tot);
static void adt7463c_refresh_volt(struct adt7463c_softc *sc);
static void adt7463c_refresh_temp(struct adt7463c_softc *sc);
static void adt7463c_refresh_fan(struct adt7463c_softc *sc);
static int adt7463c_verify(struct adt7463c_softc *sc);
static int adt7463c_match(struct device *, struct cfdata *, void *);
static void adt7463c_attach(struct device *, struct device *, void *);
CFATTACH_DECL(adt7463c, sizeof(struct adt7463c_softc),
adt7463c_match, adt7463c_attach, NULL, NULL);
static int
adt7463c_match(struct device *parent, struct cfdata *cf, void *aux)
{
struct i2c_attach_args *ia = aux;
struct adt7463c_softc sc;
sc.sc_tag = ia->ia_tag;
sc.sc_address = ia->ia_addr;
if(adt7463c_verify(&sc))
return (1);
return (0);
}
static void
adt7463c_attach(struct device *parent, struct device *self, void *aux)
{
struct adt7463c_softc *sc = (struct adt7463c_softc *)self;
struct i2c_attach_args *ia = aux;
int i = 0;
sc->sc_tag = ia->ia_tag;
sc->sc_address = ia->ia_addr;
/* start ADT7463 */
adt7463c_write_1(sc, ADT7463_CONFIG_REG1, ADT7463_START);
/* set config reg3 to enable fast TACH measurements */
adt7463c_write_1(sc, ADT7463_CONFIG_REG3, ADT7463_CONFIG_REG3_FAST);
/* begin fan speed control addition */
/* associate each fan with Temp zone 2 */
adt7463c_write_1(sc, FANZONEREG1, TEMPCHANNEL);
adt7463c_write_1(sc, FANZONEREG2, TEMPCHANNEL);
adt7463c_write_1(sc, FANZONEREG3, TEMPCHANNEL);
/* set Tmin */
adt7463c_write_1(sc, TMINREG, TMINTEMP);
/* set fans to always on when below Tmin */
adt7463c_write_1(sc, FANONREG, ALWAYSON);
/* set min fan speed */
adt7463c_write_1(sc, FANMINREG1, FANMINSPEED);
adt7463c_write_1(sc, FANMINREG2, FANMINSPEED);
adt7463c_write_1(sc, FANMINREG3, FANMINSPEED);
/* set Trange */
adt7463c_write_1(sc, TRANGEREG, TRANGEVAL);
/* set Tterm */
adt7463c_write_1(sc, TTERMREG, TTERMVAL);
/* set operating point */
adt7463c_write_1(sc, OPPTREG, OPPTTEMP);
/* set Tlow */
adt7463c_write_1(sc, TLOWREG, TLOW);
/* set Thigh */
adt7463c_write_1(sc, THIGHREG, THIGH);
/* turn on dynamic control */
adt7463c_write_1(sc, ENABLEDYNAMICREG, REMOTE2);
/* set a hyst value */
adt7463c_write_1(sc,THYSTREG, THYST);
/* done with fan speed control additions */
/* Initialize sensors */
adt7463c_setup_volt(sc, 0, ADT7463_VOLT_SENSORS_COUNT);
adt7463c_setup_temp(sc, ADT7463_VOLT_SENSORS_COUNT,
ADT7463_TEMP_SENSORS_COUNT);
adt7463c_setup_fan(sc, ADT7463_VOLT_SENSORS_COUNT+ADT7463_TEMP_SENSORS_COUNT,
ADT7463_FAN_SENSORS_COUNT);
for (i = 0; i < ADT7463_MAX_ENVSYS_RANGE; ++i) {
sc->sc_sensor[i].sensor = sc->sc_info[i].sensor = i;
sc->sc_sensor[i].validflags = (ENVSYS_FVALID|ENVSYS_FCURVALID);
sc->sc_info[i].validflags = ENVSYS_FVALID;
sc->sc_sensor[i].warnflags = ENVSYS_WARN_OK;
}
/* Hook into the System Monitor. */
sc->sc_sysmon.sme_ranges = adt7463c_ranges;
sc->sc_sysmon.sme_sensor_info = sc->sc_info;
sc->sc_sysmon.sme_sensor_data = sc->sc_sensor;
sc->sc_sysmon.sme_cookie = sc;
/* callback for envsys get data */
sc->sc_sysmon.sme_gtredata = adt7463c_gtredata;
sc->sc_sysmon.sme_nsensors = ADT7463_MAX_ENVSYS_RANGE;
sc->sc_sysmon.sme_envsys_version = 1000;
sc->sc_sysmon.sme_flags = 0;
if (sysmon_envsys_register(&sc->sc_sysmon))
printf("adt7463: unable to register with sysmon\n");
}
static int
adt7463c_verify(struct adt7463c_softc *sc)
{
/* verify this is an adt7463 */
int c_id, d_id;
adt7463c_send_1(sc, ADT7463_COMPANYID_REG);
c_id = adt7463c_receive_1(sc);
adt7463c_send_1(sc, ADT7463_DEVICEID_REG);
d_id = adt7463c_receive_1(sc);
if ( (c_id == ADT7463_COMPANYID) &&
(d_id == ADT7463_DEVICEID) ) {
return (1);
}
return (0);
}
static void
adt7463c_setup_volt(struct adt7463c_softc *sc, int start, int tot)
{
sc->sc_sensor[start+0].units = sc->sc_info[start+0].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+0].desc, sizeof(sc->sc_info[start+0].desc), "2.5V");
sc->sc_info[start+0].rfact = 10000;
sc->sc_sensor[start+1].units = sc->sc_info[start+1].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+1].desc, sizeof(sc->sc_info[start+1].desc), "VCCP");
sc->sc_info[start+1].rfact = 10000;
sc->sc_sensor[start+2].units = sc->sc_info[start+2].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+2].desc, sizeof(sc->sc_info[start+2].desc), "VCC");
sc->sc_info[start+2].rfact = 10000;
sc->sc_sensor[start+3].units = sc->sc_info[start+3].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+3].desc, sizeof(sc->sc_info[start+3].desc), "5V");
sc->sc_info[start+3].rfact = 10000;
sc->sc_sensor[start+4].units = sc->sc_info[start+4].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+4].desc, sizeof(sc->sc_info[start+4].desc), "12V");
sc->sc_info[start+4].rfact = 10000;
}
static void
adt7463c_setup_temp(struct adt7463c_softc *sc, int start, int tot)
{
sc->sc_sensor[start+0].units = sc->sc_info[start+0].units = ENVSYS_STEMP;
snprintf(sc->sc_info[start + 0].desc,
sizeof(sc->sc_info[start + 0].desc), "Temp-1");
sc->sc_sensor[start+1].units = sc->sc_info[start+1].units = ENVSYS_STEMP;
snprintf(sc->sc_info[start + 1].desc,
sizeof(sc->sc_info[start + 1].desc), "Temp-2");
sc->sc_sensor[start+2].units = sc->sc_info[start+2].units = ENVSYS_STEMP;
snprintf(sc->sc_info[start + 2].desc,
sizeof(sc->sc_info[start + 2].desc), "Temp-3");
}
static void
adt7463c_setup_fan(struct adt7463c_softc *sc, int start, int tot)
{
sc->sc_sensor[start + 0].units = ENVSYS_SFANRPM;
sc->sc_info[start + 0].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 0].desc,
sizeof(sc->sc_info[start + 0].desc), "Fan-1");
sc->sc_sensor[start + 1].units = ENVSYS_SFANRPM;
sc->sc_info[start + 1].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 1].desc,
sizeof(sc->sc_info[start + 1].desc), "Fan-2");
sc->sc_sensor[start + 2].units = ENVSYS_SFANRPM;
sc->sc_info[start + 2].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 2].desc,
sizeof(sc->sc_info[start + 2].desc), "Fan-3");
sc->sc_sensor[start + 3].units = ENVSYS_SFANRPM;
sc->sc_info[start + 3].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 3].desc,
sizeof(sc->sc_info[start + 3].desc), "Fan-4");
}
int
adt7463c_gtredata(sme, tred)
struct sysmon_envsys *sme;
struct envsys_tre_data *tred;
{
struct adt7463c_softc *sc = sme->sme_cookie;
adt7463c_refresh_volt(sc);
adt7463c_refresh_temp(sc);
adt7463c_refresh_fan(sc);
*tred = sc->sc_sensor[tred->sensor];
return (0);
}
void
adt7463c_refresh_volt(struct adt7463c_softc *sc)
{
int i;
u_int8_t reg;
int data;
float mult[] = {ADT7463_2_5V_CONST,
ADT7463_VCC_CONST,
ADT7463_3_3V_CONST,
ADT7463_5V_CONST,
ADT7463_12V_CONST};
reg = ADT7463_VOLT_REG_START;
for (i = 0; i < ADT7463_VOLT_SENSORS_COUNT; i++) {
adt7463c_send_1(sc, reg++);
data = adt7463c_receive_1(sc);
/* envstat assumes that voltage is in uVDC */
double val = (data * 1000000.0 * mult[i]);
if (data > 0)
sc->sc_sensor[i].cur.data_us = (u_int32_t)val;
else
sc->sc_sensor[i].cur.data_us = 0;
}
}
void
adt7463c_refresh_temp(struct adt7463c_softc *sc)
{
int i = 0;
u_int8_t reg;
int data;
reg = ADT7463_TEMP_REG_START;
for (i = 0; i < ADT7463_TEMP_SENSORS_COUNT; i++) {
adt7463c_send_1(sc, reg++);
data = adt7463c_receive_1(sc);
/* envstat assumes temperature is in micro kelvin */
if (data > 0)
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT].cur.data_us
= (data + ADT7463_CEL_TO_KELVIN)* 1000000;
else
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT].cur.data_us
= 0;
}
}
void
adt7463c_refresh_fan(struct adt7463c_softc *sc)
{
int i, j;
u_int8_t reg;
int data = 0;
u_int16_t val = 0;
u_char buf[2];
reg = ADT7463_FAN_REG_START;
for (i = 0; i < ADT7463_FAN_SENSORS_COUNT; i++) {
/* read LSB and then MSB */
for (j = 0; j < 2; j++) {
adt7463c_send_1(sc, reg++);
data = adt7463c_receive_1(sc);
if (data > 0)
buf[j] = data;
else
buf[j] = 0;
}
val = le16dec(buf);
#if _BYTE_ORDER == _BIG_ENDIAN
val = LE16TOH(val);
#endif
/* calculate RPM */
if (val > 0)
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT +
ADT7463_TEMP_SENSORS_COUNT].cur.data_us
= (ADT7463_RPM_CONST)/val;
else
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT +
ADT7463_TEMP_SENSORS_COUNT].cur.data_us = 0;
}
}
int
adt7463c_receive_1(struct adt7463c_softc *sc)
{
u_int8_t val = 0;
int error = 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return (error);
if ((error = iic_exec(sc->sc_tag, I2C_OP_READ,
sc->sc_address, NULL, 0, &val, 1, 0)) != 0)
return (error);
iic_release_bus(sc->sc_tag, 0);
return (val);
}
int
adt7463c_send_1(struct adt7463c_softc *sc, u_int8_t val)
{
int error = 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return (error);
if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE,
sc->sc_address, NULL, 0, &val, 1, 0)) != 0)
return (error);
iic_release_bus(sc->sc_tag, 0);
return (0);
}
int
adt7463c_write_1(struct adt7463c_softc *sc, u_int8_t cmd, u_int8_t val)
{
int error = 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return (error);
if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE,
sc->sc_address, &cmd, 1, &val, 1, 0)) != 0)
return (error);
iic_release_bus(sc->sc_tag, 0);
return (0);
}
=================
4. sys/dev/i2c/adt7463reg.h
/*
* Copyright (c) 2005 Anil Gopinath (anil_public@yahoo.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Analog devices AD7463 remote thermal controller and voltage monitor
* Data sheet at:
* http://www.analog.com/UploadedFiles/Data_Sheets/272624927ADT7463_c.pdf
*/
/* Fan speed control added by Hanns Hartman */
#ifndef INCLUDE_ADT7463REG_H
#define INCLUDE_ADT7463REG_H
#define ADT7463_VOLT_SENSORS_COUNT 5
#define ADT7463_TEMP_SENSORS_COUNT 3
#define ADT7463_FAN_SENSORS_COUNT 4
#define ADT7463_MAX_ENVSYS_RANGE 12 /* sum of the above */
/* I2C/SMBUS address */
#define ADT7463_ADDR1 0x2C
#define ADT7463_ADDR2 0x2D
#define ADT7463_ADDR3 0x2E
#define ADT7463_CONFIG_REG1 0x40
#define ADT7463_CONFIG_REG3 0x78
#define ADT7463_START 0x01
#define ADT7463_COMPANYID_REG 0x3E
#define ADT7463_COMPANYID 0x41
#define ADT7463_DEVICEID_REG 0x3D
#define ADT7463_DEVICEID 0x27
#define ADT7463_VOLT_REG_START 0x20
#define ADT7463_TEMP_REG_START 0x25
#define ADT7463_FAN_REG_START 0x28
#define ADT7463_CONFIG_REG3_FAST 0x08
/* currently we use only 8 bits and hence the multiplier */
#define ADT7463_12V_CONST (0.0625)
#define ADT7463_5V_CONST (0.0260)
#define ADT7463_3_3V_CONST (0.0171)
#define ADT7463_2_5V_CONST (0.0130)
#define ADT7463_VCC_CONST (0.0117)
#define ADT7463_CEL_TO_KELVIN 273.15
#define ADT7463_RPM_CONST (90000 * 60)
const struct envsys_range adt7463c_ranges[] = {
{ 0, 0xFF, ENVSYS_STEMP },
{ 0, 0xFF, ENVSYS_SFANRPM },
{ 1, 0, ENVSYS_SVOLTS_AC }, /* None */
{ 0, 0xFF, ENVSYS_SVOLTS_DC },
{ 1, 0, ENVSYS_SOHMS }, /* None */
{ 1, 0, ENVSYS_SWATTS }, /* None */
{ 1, 0, ENVSYS_SAMPS } /* None */
};
struct adt7463c_softc {
struct device sc_dev; /* generic device structures */
i2c_tag_t sc_tag;
i2c_addr_t sc_address;
struct envsys_tre_data sc_sensor[ADT7463_MAX_ENVSYS_RANGE];
struct envsys_basic_info sc_info[ADT7463_MAX_ENVSYS_RANGE];
struct sysmon_envsys sc_sysmon;
};
/* Fan speed control define(s)
* All below references to page numbers refer to the Automatic Fan
* Speed Control App Note
*/
/* step two setting temperature zone 2
* page 5 gives specific information about how to program the temperature
* channel. Also note that the low order byte of 2 should not be changed.
*/
#define FANZONEREG1 0x5C
#define FANZONEREG2 0x5D
#define FANZONEREG3 0x5E
#define TEMPCHANNEL 0x42
/* Minimum temperature remote zone 2 (page 7) */
#define TMINREG 0x69
#define TMINTEMP 0x2C
/* keep the fans always on
* please see page 7 for which bit to set to enable a
* pwm to be left always on.
*/
#define FANONREG 0x62
#define ALWAYSON 0xE0
/* minimum fan speed
* computing the number for FANMINSPEED is done by converting
* percent fan speed to a pwm number using the equation on page 8
*/
#define FANMINREG1 0x64
#define FANMINREG2 0x65
#define FANMINREG3 0x66
#define FANMINSPEED 0x45
/* give a Trange this is the slope at which the fan speed will
* increase based on temperature
* please make sure not to change the low order byte of 4 if adjusting
* this value. In order to calculate Trange use the equation on page 9
* note that this is the best value given the current bios situation
*/
#define TRANGEREG 0x61
#define TRANGEVAL 0x94
/* This is the hyst value. once the operating temperature-hyst
* is broken the fan speed will start to increase
* consult page 12 for what to put in what register
*/
#define THYSTREG 0x6E
#define THYST 0x20
/* this is the value when reach will cause the fans to drive at full speed
* see page 12
*/
#define TTERMREG 0x6C
#define TTERMVAL 0x39
/* This is the desired operating temperature for the cpu
* see page 15-16 for more detail
*/
#define OPPTREG 0x35
#define OPPTTEMP 0x34
/* Once the temperature falls below this point the
* fans speed will start to decrease
* see page 17 for more detail
*/
#define TLOWREG 0x52
#define TLOW 0x2D
/* Once the temperature rises above this point the fan speed
* will be increased at a more rapid rate
* see page 17 for more detail
*/
#define THIGHREG 0x53
#define THIGH 0x36
/* Enable dynamic control on remote2 given a polling interval
* please see page 18-22 in setting values for register 0x36
*/
#define ENABLEDYNAMICREG 0x36
#define REMOTE2 0x80
/* done with fan speed control additions */
#endif