Commit 1591caf2 authored by Cédric Le Goater's avatar Cédric Le Goater Committed by Jacek Anaszewski

leds: pca955x: check for I2C errors

This should also allow probing to fail when a pca955x chip is not
found on a I2C bus.
Signed-off-by: default avatarCédric Le Goater <clg@kaod.org>
Signed-off-by: default avatarJacek Anaszewski <jacek.anaszewski@gmail.com>
parent f5808ac1
...@@ -165,13 +165,18 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) ...@@ -165,13 +165,18 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
* Write to frequency prescaler register, used to program the * Write to frequency prescaler register, used to program the
* period of the PWM output. period = (PSCx + 1) / 38 * period of the PWM output. period = (PSCx + 1) / 38
*/ */
static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)
{ {
struct pca955x *pca955x = i2c_get_clientdata(client); struct pca955x *pca955x = i2c_get_clientdata(client);
int ret;
i2c_smbus_write_byte_data(client, ret = i2c_smbus_write_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n,
val); val);
if (ret < 0)
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
__func__, n, val, ret);
return ret;
} }
/* /*
...@@ -181,38 +186,56 @@ static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) ...@@ -181,38 +186,56 @@ static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
* *
* Duty cycle is (256 - PWMx) / 256 * Duty cycle is (256 - PWMx) / 256
*/ */
static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
{ {
struct pca955x *pca955x = i2c_get_clientdata(client); struct pca955x *pca955x = i2c_get_clientdata(client);
int ret;
i2c_smbus_write_byte_data(client, ret = i2c_smbus_write_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n,
val); val);
if (ret < 0)
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
__func__, n, val, ret);
return ret;
} }
/* /*
* Write to LED selector register, which determines the source that * Write to LED selector register, which determines the source that
* drives the LED output. * drives the LED output.
*/ */
static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) static int pca955x_write_ls(struct i2c_client *client, int n, u8 val)
{ {
struct pca955x *pca955x = i2c_get_clientdata(client); struct pca955x *pca955x = i2c_get_clientdata(client);
int ret;
i2c_smbus_write_byte_data(client, ret = i2c_smbus_write_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n,
val); val);
if (ret < 0)
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
__func__, n, val, ret);
return ret;
} }
/* /*
* Read the LED selector register, which determines the source that * Read the LED selector register, which determines the source that
* drives the LED output. * drives the LED output.
*/ */
static u8 pca955x_read_ls(struct i2c_client *client, int n) static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val)
{ {
struct pca955x *pca955x = i2c_get_clientdata(client); struct pca955x *pca955x = i2c_get_clientdata(client);
int ret;
return (u8) i2c_smbus_read_byte_data(client, ret = i2c_smbus_read_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
if (ret < 0) {
dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
__func__, n, ret);
return ret;
}
*val = (u8)ret;
return 0;
} }
static int pca955x_led_set(struct led_classdev *led_cdev, static int pca955x_led_set(struct led_classdev *led_cdev,
...@@ -223,6 +246,7 @@ static int pca955x_led_set(struct led_classdev *led_cdev, ...@@ -223,6 +246,7 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
u8 ls; u8 ls;
int chip_ls; /* which LSx to use (0-3 potentially) */ int chip_ls; /* which LSx to use (0-3 potentially) */
int ls_led; /* which set of bits within LSx to use (0-3) */ int ls_led; /* which set of bits within LSx to use (0-3) */
int ret;
pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev); pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev);
pca955x = pca955x_led->pca955x; pca955x = pca955x_led->pca955x;
...@@ -232,7 +256,9 @@ static int pca955x_led_set(struct led_classdev *led_cdev, ...@@ -232,7 +256,9 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
mutex_lock(&pca955x->lock); mutex_lock(&pca955x->lock);
ls = pca955x_read_ls(pca955x->client, chip_ls); ret = pca955x_read_ls(pca955x->client, chip_ls, &ls);
if (ret)
goto out;
switch (value) { switch (value) {
case LED_FULL: case LED_FULL:
...@@ -252,26 +278,37 @@ static int pca955x_led_set(struct led_classdev *led_cdev, ...@@ -252,26 +278,37 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
* OFF, HALF, or FULL. But, this is probably better than * OFF, HALF, or FULL. But, this is probably better than
* just turning off for all other values. * just turning off for all other values.
*/ */
pca955x_write_pwm(pca955x->client, 1, ret = pca955x_write_pwm(pca955x->client, 1, 255 - value);
255 - value); if (ret)
goto out;
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
break; break;
} }
pca955x_write_ls(pca955x->client, chip_ls, ls); ret = pca955x_write_ls(pca955x->client, chip_ls, ls);
out:
mutex_unlock(&pca955x->lock); mutex_unlock(&pca955x->lock);
return 0; return ret;
} }
#ifdef CONFIG_LEDS_PCA955X_GPIO #ifdef CONFIG_LEDS_PCA955X_GPIO
/* /*
* Read the INPUT register, which contains the state of LEDs. * Read the INPUT register, which contains the state of LEDs.
*/ */
static u8 pca955x_read_input(struct i2c_client *client, int n) static int pca955x_read_input(struct i2c_client *client, int n, u8 *val)
{ {
return (u8)i2c_smbus_read_byte_data(client, n); int ret = i2c_smbus_read_byte_data(client, n);
if (ret < 0) {
dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
__func__, n, ret);
return ret;
}
*val = (u8)ret;
return 0;
} }
static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset) static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
...@@ -285,23 +322,32 @@ static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset) ...@@ -285,23 +322,32 @@ static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
return -EBUSY; return -EBUSY;
} }
static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset, static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,
int val) int val)
{ {
struct pca955x *pca955x = gpiochip_get_data(gc); struct pca955x *pca955x = gpiochip_get_data(gc);
struct pca955x_led *led = &pca955x->leds[offset]; struct pca955x_led *led = &pca955x->leds[offset];
if (val) if (val)
pca955x_led_set(&led->led_cdev, LED_FULL); return pca955x_led_set(&led->led_cdev, LED_FULL);
else else
pca955x_led_set(&led->led_cdev, LED_OFF); return pca955x_led_set(&led->led_cdev, LED_OFF);
}
static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
int val)
{
pca955x_set_value(gc, offset, val);
} }
static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset) static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
{ {
struct pca955x *pca955x = gpiochip_get_data(gc); struct pca955x *pca955x = gpiochip_get_data(gc);
struct pca955x_led *led = &pca955x->leds[offset]; struct pca955x_led *led = &pca955x->leds[offset];
u8 reg = pca955x_read_input(pca955x->client, led->led_num / 8); u8 reg = 0;
/* There is nothing we can do about errors */
pca955x_read_input(pca955x->client, led->led_num / 8, &reg);
return !!(reg & (1 << (led->led_num % 8))); return !!(reg & (1 << (led->led_num % 8)));
} }
...@@ -310,17 +356,13 @@ static int pca955x_gpio_direction_input(struct gpio_chip *gc, ...@@ -310,17 +356,13 @@ static int pca955x_gpio_direction_input(struct gpio_chip *gc,
unsigned int offset) unsigned int offset)
{ {
/* To use as input ensure pin is not driven */ /* To use as input ensure pin is not driven */
pca955x_gpio_set_value(gc, offset, 0); return pca955x_set_value(gc, offset, 0);
return 0;
} }
static int pca955x_gpio_direction_output(struct gpio_chip *gc, static int pca955x_gpio_direction_output(struct gpio_chip *gc,
unsigned int offset, int val) unsigned int offset, int val)
{ {
pca955x_gpio_set_value(gc, offset, val); return pca955x_set_value(gc, offset, val);
return 0;
} }
#endif /* CONFIG_LEDS_PCA955X_GPIO */ #endif /* CONFIG_LEDS_PCA955X_GPIO */
...@@ -495,19 +537,29 @@ static int pca955x_probe(struct i2c_client *client, ...@@ -495,19 +537,29 @@ static int pca955x_probe(struct i2c_client *client,
return err; return err;
/* Turn off LED */ /* Turn off LED */
pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF);
if (err)
return err;
} }
} }
/* PWM0 is used for half brightness or 50% duty cycle */ /* PWM0 is used for half brightness or 50% duty cycle */
pca955x_write_pwm(client, 0, 255-LED_HALF); err = pca955x_write_pwm(client, 0, 255 - LED_HALF);
if (err)
return err;
/* PWM1 is used for variable brightness, default to OFF */ /* PWM1 is used for variable brightness, default to OFF */
pca955x_write_pwm(client, 1, 0); err = pca955x_write_pwm(client, 1, 0);
if (err)
return err;
/* Set to fast frequency so we do not see flashing */ /* Set to fast frequency so we do not see flashing */
pca955x_write_psc(client, 0, 0); err = pca955x_write_psc(client, 0, 0);
pca955x_write_psc(client, 1, 0); if (err)
return err;
err = pca955x_write_psc(client, 1, 0);
if (err)
return err;
#ifdef CONFIG_LEDS_PCA955X_GPIO #ifdef CONFIG_LEDS_PCA955X_GPIO
if (ngpios) { if (ngpios) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment