diff options
author | Mark A. Greer <mgreer@animalcreek.com> | 2013-07-25 13:16:41 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2013-08-26 20:22:10 -0400 |
commit | 8465b01827b7a1e0e2464b0a300618bf7add25d8 (patch) | |
tree | a06c6620291ab5a0a8e9eb7e5507e9d089ba7139 | |
parent | 33b3a561f417ec3e1013999ce8bdb6c055abb1ce (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.txt | 1 | ||||
-rw-r--r-- | drivers/leds/leds-pca9633.c | 136 | ||||
-rw-r--r-- | include/linux/platform_data/leds-pca9633.h | 6 |
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 | ||
6 | Optional properties: | 6 | Optional 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 | ||
9 | Each led is represented as a sub-node of the nxp,pca9633 device. | 10 | Each 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 | |||
39 | static const struct i2c_device_id pca9633_id[] = { | 56 | static const struct i2c_device_id pca9633_id[] = { |
40 | { "pca9633", 0 }, | 57 | { "pca9633", 0 }, |
41 | { } | 58 | { } |
42 | }; | 59 | }; |
43 | MODULE_DEVICE_TABLE(i2c, pca9633_id); | 60 | MODULE_DEVICE_TABLE(i2c, pca9633_id); |
44 | 61 | ||
62 | enum pca9633_cmd { | ||
63 | BRIGHTNESS_SET, | ||
64 | BLINK_SET, | ||
65 | }; | ||
66 | |||
45 | struct pca9633_led { | 67 | struct 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 | ||
54 | static void pca9633_led_work(struct work_struct *work) | 79 | static 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 | ||
104 | static 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 | |||
126 | static 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 | |||
81 | static void pca9633_led_set(struct led_classdev *led_cdev, | 141 | static 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 | ||
158 | static 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) |
98 | static struct pca9633_platform_data * | 217 | static struct pca9633_platform_data * |
99 | pca9633_dt_init(struct i2c_client *client) | 218 | pca9633_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 | ||
30 | enum pca9633_blink_type { | ||
31 | PCA9633_SW_BLINK, | ||
32 | PCA9633_HW_BLINK, | ||
33 | }; | ||
34 | |||
30 | struct pca9633_platform_data { | 35 | struct 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*/ |