Skip to content

Commit 575f10d

Browse files
Eddie Jameslag-linaro
authored andcommitted
leds: pca955x: Add HW blink support
Support blinking using the PCA955x chip. Use PWM0 for blinking instead of LED_HALF brightness. Since there is only one frequency and brightness register for any blinking LED, track the blink state of each LED and only support one HW blinking frequency. If another frequency is requested, fallback to software blinking. In addition, blinked LEDs can only use full brightness in order to maintain 50% duty cycle, which is required for the specified blink rate. Signed-off-by: Eddie James <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lee Jones <[email protected]>
1 parent 14ef073 commit 575f10d

File tree

1 file changed

+172
-52
lines changed

1 file changed

+172
-52
lines changed

drivers/leds/leds-pca955x.c

Lines changed: 172 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
#define PCA955X_GPIO_HIGH LED_OFF
6363
#define PCA955X_GPIO_LOW LED_FULL
6464

65+
#define PCA955X_BLINK_DEFAULT_MS 1000
66+
6567
enum pca955x_type {
6668
pca9550,
6769
pca9551,
@@ -74,33 +76,39 @@ struct pca955x_chipdef {
7476
int bits;
7577
u8 slv_addr; /* 7-bit slave address mask */
7678
int slv_addr_shift; /* Number of bits to ignore */
79+
int blink_div; /* PSC divider */
7780
};
7881

7982
static const struct pca955x_chipdef pca955x_chipdefs[] = {
8083
[pca9550] = {
8184
.bits = 2,
8285
.slv_addr = /* 110000x */ 0x60,
8386
.slv_addr_shift = 1,
87+
.blink_div = 44,
8488
},
8589
[pca9551] = {
8690
.bits = 8,
8791
.slv_addr = /* 1100xxx */ 0x60,
8892
.slv_addr_shift = 3,
93+
.blink_div = 38,
8994
},
9095
[pca9552] = {
9196
.bits = 16,
9297
.slv_addr = /* 1100xxx */ 0x60,
9398
.slv_addr_shift = 3,
99+
.blink_div = 44,
94100
},
95101
[ibm_pca9552] = {
96102
.bits = 16,
97103
.slv_addr = /* 0110xxx */ 0x30,
98104
.slv_addr_shift = 3,
105+
.blink_div = 44,
99106
},
100107
[pca9553] = {
101108
.bits = 4,
102109
.slv_addr = /* 110001x */ 0x62,
103110
.slv_addr_shift = 1,
111+
.blink_div = 44,
104112
},
105113
};
106114

@@ -109,7 +117,9 @@ struct pca955x {
109117
struct pca955x_led *leds;
110118
const struct pca955x_chipdef *chipdef;
111119
struct i2c_client *client;
120+
unsigned long active_blink;
112121
unsigned long active_pins;
122+
unsigned long blink_period;
113123
#ifdef CONFIG_LEDS_PCA955X_GPIO
114124
struct gpio_chip gpio;
115125
#endif
@@ -154,7 +164,8 @@ static inline int pca955x_ledstate(u8 ls, int led_num)
154164

155165
/*
156166
* Write to frequency prescaler register, used to program the
157-
* period of the PWM output. period = (PSCx + 1) / 38
167+
* period of the PWM output. period = (PSCx + 1) / coeff
168+
* Where for pca9551 chips coeff = 38 and for all other chips coeff = 44
158169
*/
159170
static int pca955x_write_psc(struct pca955x *pca955x, int n, u8 val)
160171
{
@@ -235,6 +246,21 @@ static int pca955x_read_pwm(struct pca955x *pca955x, int n, u8 *val)
235246
return 0;
236247
}
237248

249+
static int pca955x_read_psc(struct pca955x *pca955x, int n, u8 *val)
250+
{
251+
int ret;
252+
u8 cmd;
253+
254+
cmd = pca955x_num_input_regs(pca955x->chipdef->bits) + (2 * n);
255+
ret = i2c_smbus_read_byte_data(pca955x->client, cmd);
256+
if (ret < 0) {
257+
dev_err(&pca955x->client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret);
258+
return ret;
259+
}
260+
*val = (u8)ret;
261+
return 0;
262+
}
263+
238264
static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
239265
{
240266
struct pca955x_led *pca955x_led = led_to_pca955x(led_cdev);
@@ -248,14 +274,12 @@ static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)
248274

249275
switch (pca955x_ledstate(ls, pca955x_led->led_num % 4)) {
250276
case PCA955X_LS_LED_ON:
277+
case PCA955X_LS_BLINK0:
251278
ret = LED_FULL;
252279
break;
253280
case PCA955X_LS_LED_OFF:
254281
ret = LED_OFF;
255282
break;
256-
case PCA955X_LS_BLINK0:
257-
ret = LED_HALF;
258-
break;
259283
case PCA955X_LS_BLINK1:
260284
ret = pca955x_read_pwm(pca955x, 1, &pwm);
261285
if (ret)
@@ -283,29 +307,36 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
283307
if (ret)
284308
goto out;
285309

286-
switch (value) {
287-
case LED_FULL:
288-
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_ON);
289-
break;
290-
case LED_OFF:
291-
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
292-
break;
293-
case LED_HALF:
294-
ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK0);
295-
break;
296-
default:
297-
/*
298-
* Use PWM1 for all other values. This has the unwanted
299-
* side effect of making all LEDs on the chip share the
300-
* same brightness level if set to a value other than
301-
* OFF, HALF, or FULL. But, this is probably better than
302-
* just turning off for all other values.
303-
*/
304-
ret = pca955x_write_pwm(pca955x, 1, 255 - value);
305-
if (ret)
310+
if (test_bit(pca955x_led->led_num, &pca955x->active_blink)) {
311+
if (value == LED_OFF) {
312+
clear_bit(pca955x_led->led_num, &pca955x->active_blink);
313+
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
314+
} else {
315+
/* No variable brightness for blinking LEDs */
306316
goto out;
307-
ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK1);
308-
break;
317+
}
318+
} else {
319+
switch (value) {
320+
case LED_FULL:
321+
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_ON);
322+
break;
323+
case LED_OFF:
324+
ls = pca955x_ledsel(ls, bit, PCA955X_LS_LED_OFF);
325+
break;
326+
default:
327+
/*
328+
* Use PWM1 for all other values. This has the unwanted
329+
* side effect of making all LEDs on the chip share the
330+
* same brightness level if set to a value other than
331+
* OFF or FULL. But, this is probably better than just
332+
* turning off for all other values.
333+
*/
334+
ret = pca955x_write_pwm(pca955x, 1, 255 - value);
335+
if (ret)
336+
goto out;
337+
ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK1);
338+
break;
339+
}
309340
}
310341

311342
ret = pca955x_write_ls(pca955x, reg, ls);
@@ -316,6 +347,104 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
316347
return ret;
317348
}
318349

350+
static u8 pca955x_period_to_psc(struct pca955x *pca955x, unsigned long period)
351+
{
352+
/* psc register value = (blink period * coeff) - 1 */
353+
period *= pca955x->chipdef->blink_div;
354+
period /= MSEC_PER_SEC;
355+
period -= 1;
356+
357+
return period;
358+
}
359+
360+
static unsigned long pca955x_psc_to_period(struct pca955x *pca955x, u8 psc)
361+
{
362+
unsigned long period = psc;
363+
364+
/* blink period = (psc register value + 1) / coeff */
365+
period += 1;
366+
period *= MSEC_PER_SEC;
367+
period /= pca955x->chipdef->blink_div;
368+
369+
return period;
370+
}
371+
372+
static int pca955x_led_blink(struct led_classdev *led_cdev,
373+
unsigned long *delay_on, unsigned long *delay_off)
374+
{
375+
struct pca955x_led *pca955x_led = led_to_pca955x(led_cdev);
376+
struct pca955x *pca955x = pca955x_led->pca955x;
377+
unsigned long period = *delay_on + *delay_off;
378+
int ret = 0;
379+
380+
mutex_lock(&pca955x->lock);
381+
382+
if (period) {
383+
if (*delay_on != *delay_off) {
384+
ret = -EINVAL;
385+
goto out;
386+
}
387+
388+
if (period < pca955x_psc_to_period(pca955x, 0) ||
389+
period > pca955x_psc_to_period(pca955x, 0xff)) {
390+
ret = -EINVAL;
391+
goto out;
392+
}
393+
} else {
394+
period = pca955x->active_blink ? pca955x->blink_period :
395+
PCA955X_BLINK_DEFAULT_MS;
396+
}
397+
398+
if (!pca955x->active_blink ||
399+
pca955x->active_blink == BIT(pca955x_led->led_num) ||
400+
pca955x->blink_period == period) {
401+
u8 psc = pca955x_period_to_psc(pca955x, period);
402+
403+
if (!test_and_set_bit(pca955x_led->led_num,
404+
&pca955x->active_blink)) {
405+
u8 ls;
406+
int reg = pca955x_led->led_num / 4;
407+
int bit = pca955x_led->led_num % 4;
408+
409+
ret = pca955x_read_ls(pca955x, reg, &ls);
410+
if (ret)
411+
goto out;
412+
413+
ls = pca955x_ledsel(ls, bit, PCA955X_LS_BLINK0);
414+
ret = pca955x_write_ls(pca955x, reg, ls);
415+
if (ret)
416+
goto out;
417+
418+
/*
419+
* Force 50% duty cycle to maintain the specified
420+
* blink rate.
421+
*/
422+
ret = pca955x_write_pwm(pca955x, 0, 128);
423+
if (ret)
424+
goto out;
425+
}
426+
427+
if (pca955x->blink_period != period) {
428+
pca955x->blink_period = period;
429+
ret = pca955x_write_psc(pca955x, 0, psc);
430+
if (ret)
431+
goto out;
432+
}
433+
434+
period = pca955x_psc_to_period(pca955x, psc);
435+
period /= 2;
436+
*delay_on = period;
437+
*delay_off = period;
438+
} else {
439+
ret = -EBUSY;
440+
}
441+
442+
out:
443+
mutex_unlock(&pca955x->lock);
444+
445+
return ret;
446+
}
447+
319448
#ifdef CONFIG_LEDS_PCA955X_GPIO
320449
/*
321450
* Read the INPUT register, which contains the state of LEDs.
@@ -450,8 +579,9 @@ static int pca955x_probe(struct i2c_client *client)
450579
u8 ls1[4];
451580
u8 ls2[4];
452581
struct pca955x_platform_data *pdata;
582+
u8 psc0;
583+
bool keep_psc0 = false;
453584
bool set_default_label = false;
454-
bool keep_pwm = false;
455585
char default_label[8];
456586

457587
chip = i2c_get_match_data(client);
@@ -502,6 +632,7 @@ static int pca955x_probe(struct i2c_client *client)
502632
mutex_init(&pca955x->lock);
503633
pca955x->client = client;
504634
pca955x->chipdef = chip;
635+
pca955x->blink_period = PCA955X_BLINK_DEFAULT_MS;
505636

506637
init_data.devname_mandatory = false;
507638
init_data.devicename = "pca955x";
@@ -533,11 +664,16 @@ static int pca955x_probe(struct i2c_client *client)
533664
led = &pca955x_led->led_cdev;
534665
led->brightness_set_blocking = pca955x_led_set;
535666
led->brightness_get = pca955x_led_get;
667+
led->blink_set = pca955x_led_blink;
536668

537669
if (pdata->leds[i].default_state == LEDS_DEFSTATE_OFF)
538670
ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_OFF);
539671
else if (pdata->leds[i].default_state == LEDS_DEFSTATE_ON)
540672
ls2[reg] = pca955x_ledsel(ls2[reg], bit, PCA955X_LS_LED_ON);
673+
else if (pca955x_ledstate(ls2[reg], bit) == PCA955X_LS_BLINK0) {
674+
keep_psc0 = true;
675+
set_bit(i, &pca955x->active_blink);
676+
}
541677

542678
init_data.fwnode = pdata->leds[i].fwnode;
543679

@@ -565,19 +701,6 @@ static int pca955x_probe(struct i2c_client *client)
565701
return err;
566702

567703
set_bit(i, &pca955x->active_pins);
568-
569-
/*
570-
* For default-state == "keep", let the core update the
571-
* brightness from the hardware, then check the
572-
* brightness to see if it's using PWM1. If so, PWM1
573-
* should not be written below.
574-
*/
575-
if (pdata->leds[i].default_state == LEDS_DEFSTATE_KEEP) {
576-
if (led->brightness != LED_FULL &&
577-
led->brightness != LED_OFF &&
578-
led->brightness != LED_HALF)
579-
keep_pwm = true;
580-
}
581704
}
582705
}
583706

@@ -589,22 +712,19 @@ static int pca955x_probe(struct i2c_client *client)
589712
}
590713
}
591714

592-
/* PWM0 is used for half brightness or 50% duty cycle */
593-
err = pca955x_write_pwm(pca955x, 0, 255 - LED_HALF);
594-
if (err)
595-
return err;
596-
597-
if (!keep_pwm) {
598-
/* PWM1 is used for variable brightness, default to OFF */
599-
err = pca955x_write_pwm(pca955x, 1, 0);
600-
if (err)
601-
return err;
715+
if (keep_psc0) {
716+
err = pca955x_read_psc(pca955x, 0, &psc0);
717+
} else {
718+
psc0 = pca955x_period_to_psc(pca955x, pca955x->blink_period);
719+
err = pca955x_write_psc(pca955x, 0, psc0);
602720
}
603721

604-
/* Set to fast frequency so we do not see flashing */
605-
err = pca955x_write_psc(pca955x, 0, 0);
606722
if (err)
607723
return err;
724+
725+
pca955x->blink_period = pca955x_psc_to_period(pca955x, psc0);
726+
727+
/* Set PWM1 to fast frequency so we do not see flashing */
608728
err = pca955x_write_psc(pca955x, 1, 0);
609729
if (err)
610730
return err;

0 commit comments

Comments
 (0)