aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark A. Greer <mgreer@animalcreek.com>2013-07-25 13:16:41 -0400
committerBryan Wu <cooloney@gmail.com>2013-08-26 20:22:10 -0400
commit8465b01827b7a1e0e2464b0a300618bf7add25d8 (patch)
treea06c6620291ab5a0a8e9eb7e5507e9d089ba7139
parent33b3a561f417ec3e1013999ce8bdb6c055abb1ce (diff)
leds: pca9633: Add hardware blink support
Add hardware blinking support to the pca9633 driver. NOTE: Hardware blinking violates the leds infrastructure driver interface since the hardware only supports blinking all LEDs with the same delay_on/delay_off rates. That is, only the LEDs that are set to blink will actually blink but all LEDs that are set to blink will blink in identical fashion. The delay_on/delay_off values of the last LED that is set to blink will be used for all of the blinking LEDs. If the hardware doesn't support the requested blinking pattern, a default of 500ms on and off will be used. Hardware blinking is disabled by default but can be enabled by setting the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK' or by adding the 'nxp,hw-blink' property to the DTS. (fengguang.wu@intel.com: Removes unneeded semicolon.) Signed-off-by: Mark A. Greer <mgreer@animalcreek.com> Reported-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r--Documentation/devicetree/bindings/leds/pca9633.txt1
-rw-r--r--drivers/leds/leds-pca9633.c136
-rw-r--r--include/linux/platform_data/leds-pca9633.h6
3 files changed, 139 insertions, 4 deletions
diff --git a/Documentation/devicetree/bindings/leds/pca9633.txt b/Documentation/devicetree/bindings/leds/pca9633.txt
index 8140512c0ac8..6d9e1a9e7e9f 100644
--- a/Documentation/devicetree/bindings/leds/pca9633.txt
+++ b/Documentation/devicetree/bindings/leds/pca9633.txt
@@ -5,6 +5,7 @@ Required properties:
5 5
6Optional properties: 6Optional properties:
7- nxp,totem-pole : use totem pole (push-pull) instead of default open-drain 7- nxp,totem-pole : use totem pole (push-pull) instead of default open-drain
8- nxp,hw-blink : use hardware blinking instead of software blinking
8 9
9Each led is represented as a sub-node of the nxp,pca9633 device. 10Each led is represented as a sub-node of the nxp,pca9633 device.
10 11
diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c
index 90935e465c4d..77b3f24b7207 100644
--- a/drivers/leds/leds-pca9633.c
+++ b/drivers/leds/leds-pca9633.c
@@ -11,6 +11,15 @@
11 * 11 *
12 * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) 12 * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
13 * 13 *
14 * Note that hardware blinking violates the leds infrastructure driver
15 * interface since the hardware only supports blinking all LEDs with the
16 * same delay_on/delay_off rates. That is, only the LEDs that are set to
17 * blink will actually blink but all LEDs that are set to blink will blink
18 * in identical fashion. The delay_on/delay_off values of the last LED
19 * that is set to blink will be used for all of the blinking LEDs.
20 * Hardware blinking is disabled by default but can be enabled by setting
21 * the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK'
22 * or by adding the 'nxp,hw-blink' property to the DTS.
14 */ 23 */
15 24
16#include <linux/module.h> 25#include <linux/module.h>
@@ -31,30 +40,44 @@
31#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ 40#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */
32#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ 41#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
33 42
43#define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */
44
34#define PCA9633_MODE1 0x00 45#define PCA9633_MODE1 0x00
35#define PCA9633_MODE2 0x01 46#define PCA9633_MODE2 0x01
36#define PCA9633_PWM_BASE 0x02 47#define PCA9633_PWM_BASE 0x02
48#define PCA9633_GRPPWM 0x06
49#define PCA9633_GRPFREQ 0x07
37#define PCA9633_LEDOUT 0x08 50#define PCA9633_LEDOUT 0x08
38 51
52/* Total blink period in milliseconds */
53#define PCA9632_BLINK_PERIOD_MIN 42
54#define PCA9632_BLINK_PERIOD_MAX 10667
55
39static const struct i2c_device_id pca9633_id[] = { 56static const struct i2c_device_id pca9633_id[] = {
40 { "pca9633", 0 }, 57 { "pca9633", 0 },
41 { } 58 { }
42}; 59};
43MODULE_DEVICE_TABLE(i2c, pca9633_id); 60MODULE_DEVICE_TABLE(i2c, pca9633_id);
44 61
62enum pca9633_cmd {
63 BRIGHTNESS_SET,
64 BLINK_SET,
65};
66
45struct pca9633_led { 67struct pca9633_led {
46 struct i2c_client *client; 68 struct i2c_client *client;
47 struct work_struct work; 69 struct work_struct work;
48 enum led_brightness brightness; 70 enum led_brightness brightness;
49 struct led_classdev led_cdev; 71 struct led_classdev led_cdev;
50 int led_num; /* 0 .. 3 potentially */ 72 int led_num; /* 0 .. 3 potentially */
73 enum pca9633_cmd cmd;
51 char name[32]; 74 char name[32];
75 u8 gdc;
76 u8 gfrq;
52}; 77};
53 78
54static void pca9633_led_work(struct work_struct *work) 79static void pca9633_brightness_work(struct pca9633_led *pca9633)
55{ 80{
56 struct pca9633_led *pca9633 = container_of(work,
57 struct pca9633_led, work);
58 u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); 81 u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
59 int shift = 2 * pca9633->led_num; 82 int shift = 2 * pca9633->led_num;
60 u8 mask = 0x3 << shift; 83 u8 mask = 0x3 << shift;
@@ -78,6 +101,43 @@ static void pca9633_led_work(struct work_struct *work)
78 } 101 }
79} 102}
80 103
104static void pca9633_blink_work(struct pca9633_led *pca9633)
105{
106 u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
107 u8 mode2 = i2c_smbus_read_byte_data(pca9633->client, PCA9633_MODE2);
108 int shift = 2 * pca9633->led_num;
109 u8 mask = 0x3 << shift;
110
111 i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPPWM,
112 pca9633->gdc);
113
114 i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPFREQ,
115 pca9633->gfrq);
116
117 if (!(mode2 & PCA9633_MODE2_DMBLNK))
118 i2c_smbus_write_byte_data(pca9633->client, PCA9633_MODE2,
119 mode2 | PCA9633_MODE2_DMBLNK);
120
121 if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift))
122 i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
123 (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift));
124}
125
126static void pca9633_work(struct work_struct *work)
127{
128 struct pca9633_led *pca9633 = container_of(work,
129 struct pca9633_led, work);
130
131 switch (pca9633->cmd) {
132 case BRIGHTNESS_SET:
133 pca9633_brightness_work(pca9633);
134 break;
135 case BLINK_SET:
136 pca9633_blink_work(pca9633);
137 break;
138 }
139}
140
81static void pca9633_led_set(struct led_classdev *led_cdev, 141static void pca9633_led_set(struct led_classdev *led_cdev,
82 enum led_brightness value) 142 enum led_brightness value)
83{ 143{
@@ -85,6 +145,7 @@ static void pca9633_led_set(struct led_classdev *led_cdev,
85 145
86 pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); 146 pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
87 147
148 pca9633->cmd = BRIGHTNESS_SET;
88 pca9633->brightness = value; 149 pca9633->brightness = value;
89 150
90 /* 151 /*
@@ -94,6 +155,64 @@ static void pca9633_led_set(struct led_classdev *led_cdev,
94 schedule_work(&pca9633->work); 155 schedule_work(&pca9633->work);
95} 156}
96 157
158static int pca9633_blink_set(struct led_classdev *led_cdev,
159 unsigned long *delay_on, unsigned long *delay_off)
160{
161 struct pca9633_led *pca9633;
162 unsigned long time_on, time_off, period;
163 u8 gdc, gfrq;
164
165 pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
166
167 time_on = *delay_on;
168 time_off = *delay_off;
169
170 /* If both zero, pick reasonable defaults of 500ms each */
171 if (!time_on && !time_off) {
172 time_on = 500;
173 time_off = 500;
174 }
175
176 period = time_on + time_off;
177
178 /* If period not supported by hardware, default to someting sane. */
179 if ((period < PCA9632_BLINK_PERIOD_MIN) ||
180 (period > PCA9632_BLINK_PERIOD_MAX)) {
181 time_on = 500;
182 time_off = 500;
183 period = time_on + time_off;
184 }
185
186 /*
187 * From manual: duty cycle = (GDC / 256) ->
188 * (time_on / period) = (GDC / 256) ->
189 * GDC = ((time_on * 256) / period)
190 */
191 gdc = (time_on * 256) / period;
192
193 /*
194 * From manual: period = ((GFRQ + 1) / 24) in seconds.
195 * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) ->
196 * GFRQ = ((period * 24 / 1000) - 1)
197 */
198 gfrq = (period * 24 / 1000) - 1;
199
200 pca9633->cmd = BLINK_SET;
201 pca9633->gdc = gdc;
202 pca9633->gfrq = gfrq;
203
204 /*
205 * Must use workqueue for the actual I/O since I2C operations
206 * can sleep.
207 */
208 schedule_work(&pca9633->work);
209
210 *delay_on = time_on;
211 *delay_off = time_off;
212
213 return 0;
214}
215
97#if IS_ENABLED(CONFIG_OF) 216#if IS_ENABLED(CONFIG_OF)
98static struct pca9633_platform_data * 217static struct pca9633_platform_data *
99pca9633_dt_init(struct i2c_client *client) 218pca9633_dt_init(struct i2c_client *client)
@@ -140,6 +259,12 @@ pca9633_dt_init(struct i2c_client *client)
140 else 259 else
141 pdata->outdrv = PCA9633_OPEN_DRAIN; 260 pdata->outdrv = PCA9633_OPEN_DRAIN;
142 261
262 /* default to software blinking unless hardware blinking is specified */
263 if (of_property_read_bool(np, "nxp,hw-blink"))
264 pdata->blink_type = PCA9633_HW_BLINK;
265 else
266 pdata->blink_type = PCA9633_SW_BLINK;
267
143 return pdata; 268 return pdata;
144} 269}
145 270
@@ -206,7 +331,10 @@ static int pca9633_probe(struct i2c_client *client,
206 pca9633[i].led_cdev.name = pca9633[i].name; 331 pca9633[i].led_cdev.name = pca9633[i].name;
207 pca9633[i].led_cdev.brightness_set = pca9633_led_set; 332 pca9633[i].led_cdev.brightness_set = pca9633_led_set;
208 333
209 INIT_WORK(&pca9633[i].work, pca9633_led_work); 334 if (pdata && pdata->blink_type == PCA9633_HW_BLINK)
335 pca9633[i].led_cdev.blink_set = pca9633_blink_set;
336
337 INIT_WORK(&pca9633[i].work, pca9633_work);
210 338
211 err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); 339 err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
212 if (err < 0) 340 if (err < 0)
diff --git a/include/linux/platform_data/leds-pca9633.h b/include/linux/platform_data/leds-pca9633.h
index c5bf29b6fa7f..3c1037a81d34 100644
--- a/include/linux/platform_data/leds-pca9633.h
+++ b/include/linux/platform_data/leds-pca9633.h
@@ -27,9 +27,15 @@ enum pca9633_outdrv {
27 PCA9633_TOTEM_POLE, /* aka push-pull */ 27 PCA9633_TOTEM_POLE, /* aka push-pull */
28}; 28};
29 29
30enum pca9633_blink_type {
31 PCA9633_SW_BLINK,
32 PCA9633_HW_BLINK,
33};
34
30struct pca9633_platform_data { 35struct pca9633_platform_data {
31 struct led_platform_data leds; 36 struct led_platform_data leds;
32 enum pca9633_outdrv outdrv; 37 enum pca9633_outdrv outdrv;
38 enum pca9633_blink_type blink_type;
33}; 39};
34 40
35#endif /* __LINUX_PCA9633_H*/ 41#endif /* __LINUX_PCA9633_H*/