hwmon: (sht15) check GPIO directions

Without this patch, the SHT15 driver may fail silently with a
non-bidirectional data line and/or an input-only clock line.

This patch checks the return value of gpio_direction_* function calls
and returns the error code (if any) to the caller. If an error occurs in
the read work function (work_funct_t), we wake the queue up directly
without updating the data->state flag, to notice the waiter of the I/O
error.

The patch also makes minor cleanups: s/error_ret/unlock for some labels
and uses devm_gpio_request_one() for the clock line.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Vivien Didelot 2013-01-15 13:33:06 -05:00 committed by Guenter Roeck
parent 9e3970fba9
commit 412e29c135
1 changed files with 87 additions and 46 deletions

View File

@ -212,11 +212,13 @@ static u8 sht15_crc8(struct sht15_data *data,
* *
* This implements section 3.4 of the data sheet * This implements section 3.4 of the data sheet
*/ */
static void sht15_connection_reset(struct sht15_data *data) static int sht15_connection_reset(struct sht15_data *data)
{ {
int i; int i, err;
gpio_direction_output(data->pdata->gpio_data, 1); err = gpio_direction_output(data->pdata->gpio_data, 1);
if (err)
return err;
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
@ -226,6 +228,7 @@ static void sht15_connection_reset(struct sht15_data *data)
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
} }
return 0;
} }
/** /**
@ -251,10 +254,14 @@ static inline void sht15_send_bit(struct sht15_data *data, int val)
* conservative ones used in implementation. This implements * conservative ones used in implementation. This implements
* figure 12 on the data sheet. * figure 12 on the data sheet.
*/ */
static void sht15_transmission_start(struct sht15_data *data) static int sht15_transmission_start(struct sht15_data *data)
{ {
int err;
/* ensure data is high and output */ /* ensure data is high and output */
gpio_direction_output(data->pdata->gpio_data, 1); err = gpio_direction_output(data->pdata->gpio_data, 1);
if (err)
return err;
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
@ -270,6 +277,7 @@ static void sht15_transmission_start(struct sht15_data *data)
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
return 0;
} }
/** /**
@ -293,13 +301,19 @@ static void sht15_send_byte(struct sht15_data *data, u8 byte)
*/ */
static int sht15_wait_for_response(struct sht15_data *data) static int sht15_wait_for_response(struct sht15_data *data)
{ {
gpio_direction_input(data->pdata->gpio_data); int err;
err = gpio_direction_input(data->pdata->gpio_data);
if (err)
return err;
gpio_set_value(data->pdata->gpio_sck, 1); gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSCKH); ndelay(SHT15_TSCKH);
if (gpio_get_value(data->pdata->gpio_data)) { if (gpio_get_value(data->pdata->gpio_data)) {
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
dev_err(data->dev, "Command not acknowledged\n"); dev_err(data->dev, "Command not acknowledged\n");
sht15_connection_reset(data); err = sht15_connection_reset(data);
if (err)
return err;
return -EIO; return -EIO;
} }
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
@ -317,12 +331,13 @@ static int sht15_wait_for_response(struct sht15_data *data)
*/ */
static int sht15_send_cmd(struct sht15_data *data, u8 cmd) static int sht15_send_cmd(struct sht15_data *data, u8 cmd)
{ {
int ret = 0; int err;
sht15_transmission_start(data); err = sht15_transmission_start(data);
if (err)
return err;
sht15_send_byte(data, cmd); sht15_send_byte(data, cmd);
ret = sht15_wait_for_response(data); return sht15_wait_for_response(data);
return ret;
} }
/** /**
@ -352,9 +367,13 @@ static int sht15_soft_reset(struct sht15_data *data)
* Each byte of data is acknowledged by pulling the data line * Each byte of data is acknowledged by pulling the data line
* low for one clock pulse. * low for one clock pulse.
*/ */
static void sht15_ack(struct sht15_data *data) static int sht15_ack(struct sht15_data *data)
{ {
gpio_direction_output(data->pdata->gpio_data, 0); int err;
err = gpio_direction_output(data->pdata->gpio_data, 0);
if (err)
return err;
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1); gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
@ -362,7 +381,7 @@ static void sht15_ack(struct sht15_data *data)
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_data, 1); gpio_set_value(data->pdata->gpio_data, 1);
gpio_direction_input(data->pdata->gpio_data); return gpio_direction_input(data->pdata->gpio_data);
} }
/** /**
@ -371,14 +390,19 @@ static void sht15_ack(struct sht15_data *data)
* *
* This is basically a NAK (single clock pulse, data high). * This is basically a NAK (single clock pulse, data high).
*/ */
static void sht15_end_transmission(struct sht15_data *data) static int sht15_end_transmission(struct sht15_data *data)
{ {
gpio_direction_output(data->pdata->gpio_data, 1); int err;
err = gpio_direction_output(data->pdata->gpio_data, 1);
if (err)
return err;
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1); gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSCKH); ndelay(SHT15_TSCKH);
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
return 0;
} }
/** /**
@ -410,17 +434,19 @@ static u8 sht15_read_byte(struct sht15_data *data)
*/ */
static int sht15_send_status(struct sht15_data *data, u8 status) static int sht15_send_status(struct sht15_data *data, u8 status)
{ {
int ret; int err;
ret = sht15_send_cmd(data, SHT15_WRITE_STATUS); err = sht15_send_cmd(data, SHT15_WRITE_STATUS);
if (ret) if (err)
return ret; return err;
gpio_direction_output(data->pdata->gpio_data, 1); err = gpio_direction_output(data->pdata->gpio_data, 1);
if (err)
return err;
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
sht15_send_byte(data, status); sht15_send_byte(data, status);
ret = sht15_wait_for_response(data); err = sht15_wait_for_response(data);
if (ret) if (err)
return ret; return err;
data->val_status = status; data->val_status = status;
return 0; return 0;
@ -446,7 +472,7 @@ static int sht15_update_status(struct sht15_data *data)
|| !data->status_valid) { || !data->status_valid) {
ret = sht15_send_cmd(data, SHT15_READ_STATUS); ret = sht15_send_cmd(data, SHT15_READ_STATUS);
if (ret) if (ret)
goto error_ret; goto unlock;
status = sht15_read_byte(data); status = sht15_read_byte(data);
if (data->checksumming) { if (data->checksumming) {
@ -458,7 +484,9 @@ static int sht15_update_status(struct sht15_data *data)
== dev_checksum); == dev_checksum);
} }
sht15_end_transmission(data); ret = sht15_end_transmission(data);
if (ret)
goto unlock;
/* /*
* Perform checksum validation on the received data. * Perform checksum validation on the received data.
@ -469,27 +497,27 @@ static int sht15_update_status(struct sht15_data *data)
previous_config = data->val_status & 0x07; previous_config = data->val_status & 0x07;
ret = sht15_soft_reset(data); ret = sht15_soft_reset(data);
if (ret) if (ret)
goto error_ret; goto unlock;
if (previous_config) { if (previous_config) {
ret = sht15_send_status(data, previous_config); ret = sht15_send_status(data, previous_config);
if (ret) { if (ret) {
dev_err(data->dev, dev_err(data->dev,
"CRC validation failed, unable " "CRC validation failed, unable "
"to restore device settings\n"); "to restore device settings\n");
goto error_ret; goto unlock;
} }
} }
ret = -EAGAIN; ret = -EAGAIN;
goto error_ret; goto unlock;
} }
data->val_status = status; data->val_status = status;
data->status_valid = true; data->status_valid = true;
data->last_status = jiffies; data->last_status = jiffies;
} }
error_ret:
mutex_unlock(&data->read_lock);
unlock:
mutex_unlock(&data->read_lock);
return ret; return ret;
} }
@ -511,7 +539,9 @@ static int sht15_measurement(struct sht15_data *data,
if (ret) if (ret)
return ret; return ret;
gpio_direction_input(data->pdata->gpio_data); ret = gpio_direction_input(data->pdata->gpio_data);
if (ret)
return ret;
atomic_set(&data->interrupt_handled, 0); atomic_set(&data->interrupt_handled, 0);
enable_irq(gpio_to_irq(data->pdata->gpio_data)); enable_irq(gpio_to_irq(data->pdata->gpio_data));
@ -524,9 +554,14 @@ static int sht15_measurement(struct sht15_data *data,
ret = wait_event_timeout(data->wait_queue, ret = wait_event_timeout(data->wait_queue,
(data->state == SHT15_READING_NOTHING), (data->state == SHT15_READING_NOTHING),
msecs_to_jiffies(timeout_msecs)); msecs_to_jiffies(timeout_msecs));
if (ret == 0) {/* timeout occurred */ if (data->state != SHT15_READING_NOTHING) { /* I/O error occurred */
data->state = SHT15_READING_NOTHING;
return -EIO;
} else if (ret == 0) { /* timeout occurred */
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data); ret = sht15_connection_reset(data);
if (ret)
return ret;
return -ETIME; return -ETIME;
} }
@ -570,17 +605,17 @@ static int sht15_update_measurements(struct sht15_data *data)
data->state = SHT15_READING_HUMID; data->state = SHT15_READING_HUMID;
ret = sht15_measurement(data, SHT15_MEASURE_RH, 160); ret = sht15_measurement(data, SHT15_MEASURE_RH, 160);
if (ret) if (ret)
goto error_ret; goto unlock;
data->state = SHT15_READING_TEMP; data->state = SHT15_READING_TEMP;
ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400); ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400);
if (ret) if (ret)
goto error_ret; goto unlock;
data->measurements_valid = true; data->measurements_valid = true;
data->last_measurement = jiffies; data->last_measurement = jiffies;
} }
error_ret:
mutex_unlock(&data->read_lock);
unlock:
mutex_unlock(&data->read_lock);
return ret; return ret;
} }
@ -818,7 +853,8 @@ static void sht15_bh_read_data(struct work_struct *work_s)
/* Read the data back from the device */ /* Read the data back from the device */
val = sht15_read_byte(data); val = sht15_read_byte(data);
val <<= 8; val <<= 8;
sht15_ack(data); if (sht15_ack(data))
goto wakeup;
val |= sht15_read_byte(data); val |= sht15_read_byte(data);
if (data->checksumming) { if (data->checksumming) {
@ -826,7 +862,8 @@ static void sht15_bh_read_data(struct work_struct *work_s)
* Ask the device for a checksum and read it back. * Ask the device for a checksum and read it back.
* Note: the device sends the checksum byte reversed. * Note: the device sends the checksum byte reversed.
*/ */
sht15_ack(data); if (sht15_ack(data))
goto wakeup;
dev_checksum = sht15_reverse(sht15_read_byte(data)); dev_checksum = sht15_reverse(sht15_read_byte(data));
checksum_vals[0] = (data->state == SHT15_READING_TEMP) ? checksum_vals[0] = (data->state == SHT15_READING_TEMP) ?
SHT15_MEASURE_TEMP : SHT15_MEASURE_RH; SHT15_MEASURE_TEMP : SHT15_MEASURE_RH;
@ -837,7 +874,8 @@ static void sht15_bh_read_data(struct work_struct *work_s)
} }
/* Tell the device we are done */ /* Tell the device we are done */
sht15_end_transmission(data); if (sht15_end_transmission(data))
goto wakeup;
switch (data->state) { switch (data->state) {
case SHT15_READING_TEMP: case SHT15_READING_TEMP:
@ -851,6 +889,7 @@ static void sht15_bh_read_data(struct work_struct *work_s)
} }
data->state = SHT15_READING_NOTHING; data->state = SHT15_READING_NOTHING;
wakeup:
wake_up(&data->wait_queue); wake_up(&data->wait_queue);
} }
@ -942,17 +981,17 @@ static int sht15_probe(struct platform_device *pdev)
} }
/* Try requesting the GPIOs */ /* Try requesting the GPIOs */
ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_sck, "SHT15 sck"); ret = devm_gpio_request_one(&pdev->dev, data->pdata->gpio_sck,
GPIOF_OUT_INIT_LOW, "SHT15 sck");
if (ret) { if (ret) {
dev_err(&pdev->dev, "gpio request failed\n"); dev_err(&pdev->dev, "clock line GPIO request failed\n");
goto err_release_reg; goto err_release_reg;
} }
gpio_direction_output(data->pdata->gpio_sck, 0);
ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_data, ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_data,
"SHT15 data"); "SHT15 data");
if (ret) { if (ret) {
dev_err(&pdev->dev, "gpio request failed\n"); dev_err(&pdev->dev, "data line GPIO request failed\n");
goto err_release_reg; goto err_release_reg;
} }
@ -966,7 +1005,9 @@ static int sht15_probe(struct platform_device *pdev)
goto err_release_reg; goto err_release_reg;
} }
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data); ret = sht15_connection_reset(data);
if (ret)
goto err_release_reg;
ret = sht15_soft_reset(data); ret = sht15_soft_reset(data);
if (ret) if (ret)
goto err_release_reg; goto err_release_reg;