2
2
/* Copyright (c) 2015, Sony Mobile Communications, AB.
3
3
*/
4
4
5
+ #include <linux/delay.h>
6
+ #include <linux/interrupt.h>
7
+ #include <linux/ktime.h>
5
8
#include <linux/kernel.h>
6
9
#include <linux/backlight.h>
7
10
#include <linux/module.h>
56
59
#define WLED3_SINK_REG_STR_CABC (n ) (0x66 + (n * 0x10))
57
60
#define WLED3_SINK_REG_STR_CABC_MASK BIT(7)
58
61
62
+ /* WLED4 specific control registers */
63
+ #define WLED4_CTRL_REG_SHORT_PROTECT 0x5e
64
+ #define WLED4_CTRL_REG_SHORT_EN_MASK BIT(7)
65
+
66
+ #define WLED4_CTRL_REG_SEC_ACCESS 0xd0
67
+ #define WLED4_CTRL_REG_SEC_UNLOCK 0xa5
68
+
69
+ #define WLED4_CTRL_REG_TEST1 0xe2
70
+ #define WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 0x09
71
+
59
72
/* WLED4 specific sink registers */
60
73
#define WLED4_SINK_REG_CURR_SINK 0x46
61
74
#define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4)
@@ -105,17 +118,24 @@ struct wled_config {
105
118
bool cs_out_en ;
106
119
bool ext_gen ;
107
120
bool cabc ;
121
+ bool external_pfet ;
108
122
};
109
123
110
124
struct wled {
111
125
const char * name ;
112
126
struct device * dev ;
113
127
struct regmap * regmap ;
128
+ struct mutex lock ; /* Lock to avoid race from thread irq handler */
129
+ ktime_t last_short_event ;
114
130
u16 ctrl_addr ;
115
131
u16 sink_addr ;
116
132
u16 max_string_count ;
117
133
u32 brightness ;
118
134
u32 max_brightness ;
135
+ u32 short_count ;
136
+ bool disabled_by_short ;
137
+ bool has_short_detect ;
138
+ int short_irq ;
119
139
120
140
struct wled_config cfg ;
121
141
int (* wled_set_brightness )(struct wled * wled , u16 brightness );
@@ -166,6 +186,9 @@ static int wled_module_enable(struct wled *wled, int val)
166
186
{
167
187
int rc ;
168
188
189
+ if (wled -> disabled_by_short )
190
+ return - ENXIO ;
191
+
169
192
rc = regmap_update_bits (wled -> regmap , wled -> ctrl_addr +
170
193
WLED3_CTRL_REG_MOD_EN ,
171
194
WLED3_CTRL_REG_MOD_EN_MASK ,
@@ -202,34 +225,81 @@ static int wled_update_status(struct backlight_device *bl)
202
225
bl -> props .state & BL_CORE_FBBLANK )
203
226
brightness = 0 ;
204
227
228
+ mutex_lock (& wled -> lock );
205
229
if (brightness ) {
206
230
rc = wled -> wled_set_brightness (wled , brightness );
207
231
if (rc < 0 ) {
208
232
dev_err (wled -> dev , "wled failed to set brightness rc:%d\n" ,
209
233
rc );
210
- return rc ;
234
+ goto unlock_mutex ;
211
235
}
212
236
213
237
rc = wled_sync_toggle (wled );
214
238
if (rc < 0 ) {
215
239
dev_err (wled -> dev , "wled sync failed rc:%d\n" , rc );
216
- return rc ;
240
+ goto unlock_mutex ;
217
241
}
218
242
}
219
243
220
244
if (!!brightness != !!wled -> brightness ) {
221
245
rc = wled_module_enable (wled , !!brightness );
222
246
if (rc < 0 ) {
223
247
dev_err (wled -> dev , "wled enable failed rc:%d\n" , rc );
224
- return rc ;
248
+ goto unlock_mutex ;
225
249
}
226
250
}
227
251
228
252
wled -> brightness = brightness ;
229
253
254
+ unlock_mutex :
255
+ mutex_unlock (& wled -> lock );
256
+
230
257
return rc ;
231
258
}
232
259
260
+ #define WLED_SHORT_DLY_MS 20
261
+ #define WLED_SHORT_CNT_MAX 5
262
+ #define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC
263
+
264
+ static irqreturn_t wled_short_irq_handler (int irq , void * _wled )
265
+ {
266
+ struct wled * wled = _wled ;
267
+ int rc ;
268
+ s64 elapsed_time ;
269
+
270
+ wled -> short_count ++ ;
271
+ mutex_lock (& wled -> lock );
272
+ rc = wled_module_enable (wled , false);
273
+ if (rc < 0 ) {
274
+ dev_err (wled -> dev , "wled disable failed rc:%d\n" , rc );
275
+ goto unlock_mutex ;
276
+ }
277
+
278
+ elapsed_time = ktime_us_delta (ktime_get (),
279
+ wled -> last_short_event );
280
+ if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US )
281
+ wled -> short_count = 1 ;
282
+
283
+ if (wled -> short_count > WLED_SHORT_CNT_MAX ) {
284
+ dev_err (wled -> dev , "Short trigged %d times, disabling WLED forever!\n" ,
285
+ wled -> short_count );
286
+ wled -> disabled_by_short = true;
287
+ goto unlock_mutex ;
288
+ }
289
+
290
+ wled -> last_short_event = ktime_get ();
291
+
292
+ msleep (WLED_SHORT_DLY_MS );
293
+ rc = wled_module_enable (wled , true);
294
+ if (rc < 0 )
295
+ dev_err (wled -> dev , "wled enable failed rc:%d\n" , rc );
296
+
297
+ unlock_mutex :
298
+ mutex_unlock (& wled -> lock );
299
+
300
+ return IRQ_HANDLED ;
301
+ }
302
+
233
303
static int wled3_setup (struct wled * wled )
234
304
{
235
305
u16 addr ;
@@ -318,7 +388,7 @@ static int wled4_setup(struct wled *wled)
318
388
int rc , temp , i , j ;
319
389
u16 addr ;
320
390
u8 sink_en = 0 ;
321
- u32 sink_cfg = 0 ;
391
+ u32 sink_cfg ;
322
392
323
393
rc = regmap_update_bits (wled -> regmap ,
324
394
wled -> ctrl_addr + WLED3_CTRL_REG_OVP ,
@@ -340,6 +410,21 @@ static int wled4_setup(struct wled *wled)
340
410
if (rc < 0 )
341
411
return rc ;
342
412
413
+ if (wled -> cfg .external_pfet ) {
414
+ /* Unlock the secure register access */
415
+ rc = regmap_write (wled -> regmap , wled -> ctrl_addr +
416
+ WLED4_CTRL_REG_SEC_ACCESS ,
417
+ WLED4_CTRL_REG_SEC_UNLOCK );
418
+ if (rc < 0 )
419
+ return rc ;
420
+
421
+ rc = regmap_write (wled -> regmap ,
422
+ wled -> ctrl_addr + WLED4_CTRL_REG_TEST1 ,
423
+ WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 );
424
+ if (rc < 0 )
425
+ return rc ;
426
+ }
427
+
343
428
rc = regmap_read (wled -> regmap , wled -> sink_addr +
344
429
WLED4_SINK_REG_CURR_SINK , & sink_cfg );
345
430
if (rc < 0 )
@@ -425,6 +510,7 @@ static const struct wled_config wled4_config_defaults = {
425
510
.num_strings = 4 ,
426
511
.switch_freq = 11 ,
427
512
.cabc = false,
513
+ .external_pfet = false,
428
514
};
429
515
430
516
static const u32 wled3_boost_i_limit_values [] = {
@@ -590,6 +676,7 @@ static int wled_configure(struct wled *wled, int version)
590
676
{ "qcom,cs-out" , & cfg -> cs_out_en , },
591
677
{ "qcom,ext-gen" , & cfg -> ext_gen , },
592
678
{ "qcom,cabc" , & cfg -> cabc , },
679
+ { "qcom,external-pfet" , & cfg -> external_pfet , },
593
680
};
594
681
595
682
prop_addr = of_get_address (dev -> of_node , 0 , NULL , NULL );
@@ -678,6 +765,38 @@ static int wled_configure(struct wled *wled, int version)
678
765
return 0 ;
679
766
}
680
767
768
+ static int wled_configure_short_irq (struct wled * wled ,
769
+ struct platform_device * pdev )
770
+ {
771
+ int rc ;
772
+
773
+ if (!wled -> has_short_detect )
774
+ return 0 ;
775
+
776
+ rc = regmap_update_bits (wled -> regmap , wled -> ctrl_addr +
777
+ WLED4_CTRL_REG_SHORT_PROTECT ,
778
+ WLED4_CTRL_REG_SHORT_EN_MASK ,
779
+ WLED4_CTRL_REG_SHORT_EN_MASK );
780
+ if (rc < 0 )
781
+ return rc ;
782
+
783
+ wled -> short_irq = platform_get_irq_byname (pdev , "short" );
784
+ if (wled -> short_irq < 0 ) {
785
+ dev_dbg (& pdev -> dev , "short irq is not used\n" );
786
+ return 0 ;
787
+ }
788
+
789
+ rc = devm_request_threaded_irq (wled -> dev , wled -> short_irq ,
790
+ NULL , wled_short_irq_handler ,
791
+ IRQF_ONESHOT ,
792
+ "wled_short_irq" , wled );
793
+ if (rc < 0 )
794
+ dev_err (wled -> dev , "Unable to request short_irq (err:%d)\n" ,
795
+ rc );
796
+
797
+ return rc ;
798
+ }
799
+
681
800
static const struct backlight_ops wled_ops = {
682
801
.update_status = wled_update_status ,
683
802
};
@@ -711,6 +830,7 @@ static int wled_probe(struct platform_device *pdev)
711
830
return - ENODEV ;
712
831
}
713
832
833
+ mutex_init (& wled -> lock );
714
834
rc = wled_configure (wled , version );
715
835
if (rc )
716
836
return rc ;
@@ -725,6 +845,7 @@ static int wled_probe(struct platform_device *pdev)
725
845
break ;
726
846
727
847
case 4 :
848
+ wled -> has_short_detect = true;
728
849
rc = wled4_setup (wled );
729
850
if (rc ) {
730
851
dev_err (& pdev -> dev , "wled4_setup failed\n" );
@@ -737,6 +858,10 @@ static int wled_probe(struct platform_device *pdev)
737
858
break ;
738
859
}
739
860
861
+ rc = wled_configure_short_irq (wled , pdev );
862
+ if (rc < 0 )
863
+ return rc ;
864
+
740
865
val = WLED_DEFAULT_BRIGHTNESS ;
741
866
of_property_read_u32 (pdev -> dev .of_node , "default-brightness" , & val );
742
867
@@ -750,6 +875,16 @@ static int wled_probe(struct platform_device *pdev)
750
875
return PTR_ERR_OR_ZERO (bl );
751
876
};
752
877
878
+ static int wled_remove (struct platform_device * pdev )
879
+ {
880
+ struct wled * wled = dev_get_drvdata (& pdev -> dev );
881
+
882
+ mutex_destroy (& wled -> lock );
883
+ disable_irq (wled -> short_irq );
884
+
885
+ return 0 ;
886
+ }
887
+
753
888
static const struct of_device_id wled_match_table [] = {
754
889
{ .compatible = "qcom,pm8941-wled" , .data = (void * )3 },
755
890
{ .compatible = "qcom,pmi8998-wled" , .data = (void * )4 },
@@ -760,6 +895,7 @@ MODULE_DEVICE_TABLE(of, wled_match_table);
760
895
761
896
static struct platform_driver wled_driver = {
762
897
.probe = wled_probe ,
898
+ .remove = wled_remove ,
763
899
.driver = {
764
900
.name = "qcom,wled" ,
765
901
.of_match_table = wled_match_table ,
0 commit comments