- Improve error handling.

- Add retry loops in the i2c read/write functions.
- Combied the ADC channel selection and readout of the value into
  one iicbus_transfer to avoid possible races.

Reviewed by: nwhitehorn
This commit is contained in:
Andreas Tobler 2011-06-08 16:00:30 +00:00
parent f33b8de1dc
commit d91c258074

View File

@ -51,8 +51,6 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <powerpc/powermac/powermac_thermal.h>
#define FCU_ZERO_C_TO_K 2732
/* CPU A/B sensors, temp and adc: AD7417. */
#define AD7417_TEMP 0x00
@ -73,6 +71,16 @@ struct ad7417_sensor {
} type;
};
struct write_data {
uint8_t reg;
uint8_t val;
};
struct read_data {
uint8_t reg;
uint16_t val;
};
/* Regular bus attachment functions */
static int ad7417_probe(device_t);
static int ad7417_attach(device_t);
@ -85,6 +93,8 @@ static int ad7417_read_1(device_t dev, uint32_t addr, uint8_t reg,
uint8_t *data);
static int ad7417_read_2(device_t dev, uint32_t addr, uint8_t reg,
uint16_t *data);
static int ad7417_write_read(device_t dev, uint32_t addr,
struct write_data out, struct read_data *in);
static int ad7417_diode_read(struct ad7417_sensor *sens);
static int ad7417_adc_read(struct ad7417_sensor *sens);
static int ad7417_sensor_read(struct ad7417_sensor *sens);
@ -118,6 +128,8 @@ static int
ad7417_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff, int len)
{
unsigned char buf[4];
int try = 0;
struct iic_msg msg[] = {
{ addr, IIC_M_WR, 0, buf }
};
@ -126,76 +138,134 @@ ad7417_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff, int len)
buf[0] = reg;
memcpy(buf + 1, buff, len);
if (iicbus_transfer(dev, msg, 1) != 0) {
device_printf(dev, "iicbus write failed\n");
return (EIO);
for (;;)
{
if (iicbus_transfer(dev, msg, 1) == 0)
return (0);
if (++try > 5) {
device_printf(dev, "iicbus write failed\n");
return (-1);
}
pause("ad7417_write", hz);
}
return (0);
}
static int
ad7417_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
{
uint8_t buf[4];
int err, try = 0;
struct iic_msg msg[2] = {
{ addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
{ addr, IIC_M_RD, 1, buf },
};
if (iicbus_transfer(dev, msg, 2) != 0) {
device_printf(dev, "iicbus read failed\n");
return (EIO);
for (;;)
{
err = iicbus_transfer(dev, msg, 2);
if (err != 0)
goto retry;
*data = *((uint8_t*)buf);
return (0);
retry:
if (++try > 5) {
device_printf(dev, "iicbus read failed\n");
return (-1);
}
pause("ad7417_read_1", hz);
}
*data = *((uint8_t*)buf);
return (0);
}
static int
ad7417_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data)
{
uint8_t buf[4];
int err, try = 0;
struct iic_msg msg[2] = {
{ addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
{ addr, IIC_M_RD, 2, buf },
};
if (iicbus_transfer(dev, msg, 2) != 0) {
device_printf(dev, "iicbus read failed\n");
return (EIO);
for (;;)
{
err = iicbus_transfer(dev, msg, 2);
if (err != 0)
goto retry;
*data = *((uint16_t*)buf);
return (0);
retry:
if (++try > 5) {
device_printf(dev, "iicbus read failed\n");
return (-1);
}
pause("ad7417_read_2", hz);
}
}
*data = *((uint16_t*)buf);
static int
ad7417_write_read(device_t dev, uint32_t addr, struct write_data out,
struct read_data *in)
{
uint8_t buf[4];
int err, try = 0;
return (0);
/* Do a combined write/read. */
struct iic_msg msg[3] = {
{ addr, IIC_M_WR, 2, buf },
{ addr, IIC_M_WR | IIC_M_NOSTOP, 1, &in->reg },
{ addr, IIC_M_RD, 2, buf },
};
/* Prepare the write msg. */
buf[0] = out.reg;
buf[1] = out.val & 0xff;
for (;;)
{
err = iicbus_transfer(dev, msg, 3);
if (err != 0)
goto retry;
in->val = *((uint16_t*)buf);
return (0);
retry:
if (++try > 5) {
device_printf(dev, "iicbus write/read failed\n");
return (-1);
}
pause("ad7417_write_read", hz);
}
}
static int
ad7417_init_adc(device_t dev, uint32_t addr)
{
uint8_t buf;
int err;
adc741x_config = 0;
/* Clear Config2 */
buf = 0;
ad7417_write(dev, addr, AD7417_CONFIG2, &buf, 1);
err = ad7417_write(dev, addr, AD7417_CONFIG2, &buf, 1);
/* Read & cache Config1 */
buf = 0;
ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1);
ad7417_read_1(dev, addr, AD7417_CONFIG, &buf);
err = ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1);
err = ad7417_read_1(dev, addr, AD7417_CONFIG, &buf);
adc741x_config = (uint8_t)buf;
/* Disable shutdown mode */
adc741x_config &= 0xfe;
buf = adc741x_config;
ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1);
err = ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1);
if (err < 0)
return (-1);
return (0);
@ -300,8 +370,8 @@ ad7417_fill_sensor_prop(device_t dev)
continue;
/* Make up some ranges */
sc->sc_sensors[j].therm.target_temp = 500 + 2732;
sc->sc_sensors[j].therm.max_temp = 900 + 2732;
sc->sc_sensors[j].therm.target_temp = 500 + ZERO_C_TO_K;
sc->sc_sensors[j].therm.max_temp = 900 + ZERO_C_TO_K;
pmac_thermal_sensor_register(&sc->sc_sensors[j].therm);
}
@ -391,8 +461,13 @@ ad7417_get_temp(device_t dev, uint32_t addr, int *temp)
{
uint16_t buf[2];
uint16_t read;
int err;
err = ad7417_read_2(dev, addr, AD7417_TEMP, buf);
if (err < 0)
return (-1);
ad7417_read_2(dev, addr, AD7417_TEMP, buf);
read = *((int16_t*)buf);
/* The ADC is 10 bit, the resolution is 0.25 C.
@ -406,22 +481,25 @@ static int
ad7417_get_adc(device_t dev, uint32_t addr, unsigned int *value,
uint8_t chan)
{
uint8_t cfg1, tmp;
uint16_t read, buf[2];
ad7417_read_1(dev, addr, AD7417_CONFIG, &cfg1);
uint8_t tmp;
int err;
struct write_data config;
struct read_data data;
tmp = chan << 5;
config.reg = AD7417_CONFIG;
data.reg = AD7417_ADC;
data.val = 0;
cfg1 = (cfg1 & ~AD7417_CONFMASK) | (tmp & AD7417_CONFMASK);
err = ad7417_read_1(dev, addr, AD7417_CONFIG, &config.val);
ad7417_write(dev, addr, AD7417_CONFIG, &cfg1, 1);
config.val = (config.val & ~AD7417_CONFMASK) | (tmp & AD7417_CONFMASK);
ad7417_read_2(dev, addr, AD7417_ADC, buf);
err = ad7417_write_read(dev, addr, config, &data);
if (err < 0)
return (-1);
read = *((uint16_t*)buf);
*value = ((uint32_t)read) >> 6;
*value = ((uint32_t)data.val) >> 6;
return (0);
}
@ -444,6 +522,9 @@ ad7417_diode_read(struct ad7417_sensor *sens)
}
rawval = ad7417_adc_read(sens);
if (rawval < 0)
return (-1);
if (strstr(sens->therm.name, "CPU B") != NULL) {
diode_slope = eeprom[1][0x11] >> 16;
diode_offset = (int16_t)(eeprom[1][0x11] & 0xffff) << 12;
@ -455,7 +536,7 @@ ad7417_diode_read(struct ad7417_sensor *sens)
temp = (rawval*diode_slope + diode_offset) >> 2;
temp = (10*(temp >> 16)) + ((10*(temp & 0xffff)) >> 16);
return (temp + FCU_ZERO_C_TO_K);
return (temp + ZERO_C_TO_K);
}
static int
@ -488,7 +569,8 @@ ad7417_adc_read(struct ad7417_sensor *sens)
chan = 1;
}
ad7417_get_adc(sc->sc_dev, sc->sc_addr, &temp, chan);
if (ad7417_get_adc(sc->sc_dev, sc->sc_addr, &temp, chan) < 0)
return (-1);
return (temp);
}
@ -503,11 +585,13 @@ ad7417_sensor_read(struct ad7417_sensor *sens)
sc = device_get_softc(sens->dev);
/* Init the ADC. */
ad7417_init_adc(sc->sc_dev, sc->sc_addr);
if (ad7417_init_adc(sc->sc_dev, sc->sc_addr) < 0)
return (-1);
if (sens->type == ADC7417_TEMP_SENSOR) {
ad7417_get_temp(sc->sc_dev, sc->sc_addr, &temp);
temp += FCU_ZERO_C_TO_K;
if (ad7417_get_temp(sc->sc_dev, sc->sc_addr, &temp) < 0)
return (-1);
temp += ZERO_C_TO_K;
} else {
temp = ad7417_adc_read(sens);
}