1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
|
// SPDX-License-Identifier: GPL-2.0+
/*
* IIO driver for PAC1921 High-Side Power/Current Monitor
*
* Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
*/
#include <linux/unaligned.h>
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regmap.h>
#include <linux/units.h>
/* pac1921 registers */
#define PAC1921_REG_GAIN_CFG 0x00
#define PAC1921_REG_INT_CFG 0x01
#define PAC1921_REG_CONTROL 0x02
#define PAC1921_REG_VBUS 0x10
#define PAC1921_REG_VSENSE 0x12
#define PAC1921_REG_OVERFLOW_STS 0x1C
#define PAC1921_REG_VPOWER 0x1D
/* pac1921 gain configuration bits */
#define PAC1921_GAIN_DI_GAIN_MASK GENMASK(5, 3)
#define PAC1921_GAIN_DV_GAIN_MASK GENMASK(2, 0)
/* pac1921 integration configuration bits */
#define PAC1921_INT_CFG_SMPL_MASK GENMASK(7, 4)
#define PAC1921_INT_CFG_VSFEN BIT(3)
#define PAC1921_INT_CFG_VBFEN BIT(2)
#define PAC1921_INT_CFG_RIOV BIT(1)
#define PAC1921_INT_CFG_INTEN BIT(0)
/* pac1921 control bits */
#define PAC1921_CONTROL_MXSL_MASK GENMASK(7, 6)
enum pac1921_mxsl {
PAC1921_MXSL_VPOWER_PIN = 0,
PAC1921_MXSL_VSENSE_FREE_RUN = 1,
PAC1921_MXSL_VBUS_FREE_RUN = 2,
PAC1921_MXSL_VPOWER_FREE_RUN = 3,
};
#define PAC1921_CONTROL_SLEEP BIT(2)
/* pac1921 result registers mask and resolution */
#define PAC1921_RES_MASK GENMASK(15, 6)
#define PAC1921_RES_RESOLUTION 1023
/* pac1921 overflow status bits */
#define PAC1921_OVERFLOW_VSOV BIT(2)
#define PAC1921_OVERFLOW_VBOV BIT(1)
#define PAC1921_OVERFLOW_VPOV BIT(0)
/* pac1921 constants */
#define PAC1921_MAX_VSENSE_MV 100
#define PAC1921_MAX_VBUS_V 32
/* Time to first communication after power up (tINT_T) */
#define PAC1921_POWERUP_TIME_MS 20
/* Time from Sleep State to Start of Integration Period (tSLEEP_TO_INT) */
#define PAC1921_SLEEP_TO_INT_TIME_US 86
/* pac1921 defaults */
#define PAC1921_DEFAULT_DV_GAIN 0 /* 2^(value): 1x gain (HW default) */
#define PAC1921_DEFAULT_DI_GAIN 0 /* 2^(value): 1x gain (HW default) */
#define PAC1921_DEFAULT_NUM_SAMPLES 0 /* 2^(value): 1 sample (HW default) */
/*
* Pre-computed scale factors for BUS voltage
* format: IIO_VAL_INT_PLUS_NANO
* unit: mV
*
* Vbus scale (mV) = max_vbus (mV) / dv_gain / resolution
*/
static const int pac1921_vbus_scales[][2] = {
{ 31, 280547409 }, /* dv_gain x1 */
{ 15, 640273704 }, /* dv_gain x2 */
{ 7, 820136852 }, /* dv_gain x4 */
{ 3, 910068426 }, /* dv_gain x8 */
{ 1, 955034213 }, /* dv_gain x16 */
{ 0, 977517106 }, /* dv_gain x32 */
};
/*
* Pre-computed scales for SENSE voltage
* format: IIO_VAL_INT_PLUS_NANO
* unit: mV
*
* Vsense scale (mV) = max_vsense (mV) / di_gain / resolution
*/
static const int pac1921_vsense_scales[][2] = {
{ 0, 97751710 }, /* di_gain x1 */
{ 0, 48875855 }, /* di_gain x2 */
{ 0, 24437927 }, /* di_gain x4 */
{ 0, 12218963 }, /* di_gain x8 */
{ 0, 6109481 }, /* di_gain x16 */
{ 0, 3054740 }, /* di_gain x32 */
{ 0, 1527370 }, /* di_gain x64 */
{ 0, 763685 }, /* di_gain x128 */
};
/*
* Numbers of samples used to integrate measurements at the end of an
* integration period.
*
* Changing the number of samples affects the integration period: higher the
* number of samples, longer the integration period.
*
* These correspond to the oversampling ratios available exposed to userspace.
*/
static const int pac1921_int_num_samples[] = {
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048
};
/*
* The integration period depends on the configuration of number of integration
* samples, measurement resolution and post filters. The following array
* contains integration periods, in microsecs unit, based on table 4-5 from
* datasheet considering power integration mode, 14-Bit resolution and post
* filters on. Each index corresponds to a specific number of samples from 1
* to 2048.
*/
static const unsigned int pac1921_int_periods_usecs[] = {
2720, /* 1 sample */
4050, /* 2 samples */
6790, /* 4 samples */
12200, /* 8 samples */
23000, /* 16 samples */
46000, /* 32 samples */
92000, /* 64 samples */
184000, /* 128 samples */
368000, /* 256 samples */
736000, /* 512 samples */
1471000, /* 1024 samples */
2941000 /* 2048 samples */
};
/* pac1921 regmap configuration */
static const struct regmap_range pac1921_regmap_wr_ranges[] = {
regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL),
};
static const struct regmap_access_table pac1921_regmap_wr_table = {
.yes_ranges = pac1921_regmap_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(pac1921_regmap_wr_ranges),
};
static const struct regmap_range pac1921_regmap_rd_ranges[] = {
regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL),
regmap_reg_range(PAC1921_REG_VBUS, PAC1921_REG_VPOWER + 1),
};
static const struct regmap_access_table pac1921_regmap_rd_table = {
.yes_ranges = pac1921_regmap_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(pac1921_regmap_rd_ranges),
};
static const struct regmap_config pac1921_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &pac1921_regmap_rd_table,
.wr_table = &pac1921_regmap_wr_table,
};
enum pac1921_channels {
PAC1921_CHAN_VBUS = 0,
PAC1921_CHAN_VSENSE = 1,
PAC1921_CHAN_CURRENT = 2,
PAC1921_CHAN_POWER = 3,
};
#define PAC1921_NUM_MEAS_CHANS 4
struct pac1921_priv {
struct i2c_client *client;
struct regmap *regmap;
struct regulator *vdd;
struct iio_info iio_info;
/*
* Synchronize access to private members, and ensure atomicity of
* consecutive regmap operations.
*/
struct mutex lock;
u32 rshunt_uohm; /* uOhm */
u8 dv_gain;
u8 di_gain;
u8 n_samples;
u8 prev_ovf_flags;
u8 ovf_enabled_events;
bool first_integr_started;
bool first_integr_done;
unsigned long integr_started_time_jiffies;
unsigned int integr_period_usecs;
int current_scales[ARRAY_SIZE(pac1921_vsense_scales)][2];
struct {
u16 chan[PAC1921_NUM_MEAS_CHANS];
s64 timestamp __aligned(8);
} scan;
};
/*
* Check if first integration after configuration update has completed.
*
* Must be called with lock held.
*/
static bool pac1921_data_ready(struct pac1921_priv *priv)
{
if (!priv->first_integr_started)
return false;
if (!priv->first_integr_done) {
unsigned long t_ready;
/*
* Data valid after the device entered into integration state,
* considering worst case where the device was in sleep state,
* and completed the first integration period.
*/
t_ready = priv->integr_started_time_jiffies +
usecs_to_jiffies(PAC1921_SLEEP_TO_INT_TIME_US) +
usecs_to_jiffies(priv->integr_period_usecs);
if (time_before(jiffies, t_ready))
return false;
priv->first_integr_done = true;
}
return true;
}
static inline void pac1921_calc_scale(int dividend, int divisor, int *val,
int *val2)
{
s64 tmp;
tmp = div_s64(dividend * (s64)NANO, divisor);
*val = div_s64_rem(tmp, NANO, val2);
}
/*
* Fill the table of scale factors for current
* format: IIO_VAL_INT_PLUS_NANO
* unit: mA
*
* Vsense LSB (nV) = max_vsense (nV) * di_gain / resolution
* Current scale (mA) = Vsense LSB (nV) / shunt (uOhm)
*
* Must be called with held lock when updating after first initialization.
*/
static void pac1921_calc_current_scales(struct pac1921_priv *priv)
{
for (unsigned int i = 0; i < ARRAY_SIZE(priv->current_scales); i++) {
int max = (PAC1921_MAX_VSENSE_MV * MICRO) >> i;
int vsense_lsb = DIV_ROUND_CLOSEST(max, PAC1921_RES_RESOLUTION);
pac1921_calc_scale(vsense_lsb, priv->rshunt_uohm,
&priv->current_scales[i][0],
&priv->current_scales[i][1]);
}
}
/*
* Check if overflow occurred and if so, push the corresponding events.
*
* Must be called with lock held.
*/
static int pac1921_check_push_overflow(struct iio_dev *indio_dev, s64 timestamp)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
unsigned int flags;
int ret;
ret = regmap_read(priv->regmap, PAC1921_REG_OVERFLOW_STS, &flags);
if (ret)
return ret;
if (flags & PAC1921_OVERFLOW_VBOV &&
!(priv->prev_ovf_flags & PAC1921_OVERFLOW_VBOV) &&
priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(
IIO_VOLTAGE, PAC1921_CHAN_VBUS,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
timestamp);
}
if (flags & PAC1921_OVERFLOW_VSOV &&
!(priv->prev_ovf_flags & PAC1921_OVERFLOW_VSOV) &&
priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(
IIO_VOLTAGE, PAC1921_CHAN_VSENSE,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
timestamp);
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(
IIO_CURRENT, PAC1921_CHAN_CURRENT,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
timestamp);
}
if (flags & PAC1921_OVERFLOW_VPOV &&
!(priv->prev_ovf_flags & PAC1921_OVERFLOW_VPOV) &&
priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(
IIO_POWER, PAC1921_CHAN_POWER,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
timestamp);
}
priv->prev_ovf_flags = flags;
return 0;
}
/*
* Read the value from a result register
*
* Result registers contain the most recent averaged values of Vbus, Vsense and
* Vpower. Each value is 10 bits wide and spread across two consecutive 8 bit
* registers, with 6 bit LSB zero padding.
*/
static int pac1921_read_res(struct pac1921_priv *priv, unsigned long reg,
u16 *val)
{
int ret = regmap_bulk_read(priv->regmap, reg, val, sizeof(*val));
if (ret)
return ret;
*val = FIELD_GET(PAC1921_RES_MASK, get_unaligned_be16(val));
return 0;
}
static int pac1921_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
guard(mutex)(&priv->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW: {
s64 ts;
u16 res_val;
int ret;
if (!pac1921_data_ready(priv))
return -EBUSY;
ts = iio_get_time_ns(indio_dev);
ret = pac1921_check_push_overflow(indio_dev, ts);
if (ret)
return ret;
ret = pac1921_read_res(priv, chan->address, &res_val);
if (ret)
return ret;
*val = res_val;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->channel) {
case PAC1921_CHAN_VBUS:
*val = pac1921_vbus_scales[priv->dv_gain][0];
*val2 = pac1921_vbus_scales[priv->dv_gain][1];
return IIO_VAL_INT_PLUS_NANO;
case PAC1921_CHAN_VSENSE:
*val = pac1921_vsense_scales[priv->di_gain][0];
*val2 = pac1921_vsense_scales[priv->di_gain][1];
return IIO_VAL_INT_PLUS_NANO;
case PAC1921_CHAN_CURRENT:
*val = priv->current_scales[priv->di_gain][0];
*val2 = priv->current_scales[priv->di_gain][1];
return IIO_VAL_INT_PLUS_NANO;
case PAC1921_CHAN_POWER: {
/*
* Power scale factor in mW:
* Current scale (mA) * max_vbus (V) / dv_gain
*/
/* Get current scale based on di_gain */
int *curr_scale = priv->current_scales[priv->di_gain];
/* Convert current_scale from INT_PLUS_NANO to INT */
s64 tmp = curr_scale[0] * (s64)NANO + curr_scale[1];
/* Multiply by max_vbus (V) / dv_gain */
tmp *= PAC1921_MAX_VBUS_V >> priv->dv_gain;
/* Convert back to INT_PLUS_NANO */
*val = div_s64_rem(tmp, NANO, val2);
return IIO_VAL_INT_PLUS_NANO;
}
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = pac1921_int_num_samples[priv->n_samples];
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
/*
* The sampling frequency (Hz) is read-only and corresponds to
* how often the device provides integrated measurements into
* the result registers, thus it's 1/integration_period.
* The integration period depends on the number of integration
* samples, measurement resolution and post filters.
*
* 1/(integr_period_usecs/MICRO) = MICRO/integr_period_usecs
*/
*val = MICRO;
*val2 = priv->integr_period_usecs;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
static int pac1921_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*type = IIO_VAL_INT;
*vals = pac1921_int_num_samples;
*length = ARRAY_SIZE(pac1921_int_num_samples);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
/*
* Perform configuration update sequence: set the device into read state, then
* write the config register and set the device back into integration state.
* Also reset integration start time and mark first integration to be yet
* completed.
*
* Must be called with lock held.
*/
static int pac1921_update_cfg_reg(struct pac1921_priv *priv, unsigned int reg,
unsigned int mask, unsigned int val)
{
/* Enter READ state before configuration */
int ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
PAC1921_INT_CFG_INTEN, 0);
if (ret)
return ret;
/* Update configuration value */
ret = regmap_update_bits(priv->regmap, reg, mask, val);
if (ret)
return ret;
/* Re-enable integration */
ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN);
if (ret)
return ret;
/*
* Reset integration started time and mark this integration period as
* the first one so that new measurements will be considered as valid
* only at the end of this integration period.
*/
priv->integr_started_time_jiffies = jiffies;
priv->first_integr_done = false;
return 0;
}
/*
* Retrieve the index of the given scale (represented by scale_val and
* scale_val2) from scales_tbl. The returned index (if found) is the log2 of
* the gain corresponding to the given scale.
*
* Must be called with lock held if the scales_tbl can change runtime (e.g. for
* the current scales table)
*/
static int pac1921_lookup_scale(const int (*const scales_tbl)[2], size_t size,
int scale_val, int scale_val2)
{
for (unsigned int i = 0; i < size; i++)
if (scales_tbl[i][0] == scale_val &&
scales_tbl[i][1] == scale_val2)
return i;
return -EINVAL;
}
/*
* Configure device with the given gain (only if changed)
*
* Must be called with lock held.
*/
static int pac1921_update_gain(struct pac1921_priv *priv, u8 *priv_val, u8 gain,
unsigned int mask)
{
unsigned int reg_val;
int ret;
if (*priv_val == gain)
return 0;
reg_val = (gain << __ffs(mask)) & mask;
ret = pac1921_update_cfg_reg(priv, PAC1921_REG_GAIN_CFG, mask, reg_val);
if (ret)
return ret;
*priv_val = gain;
return 0;
}
/*
* Given a scale factor represented by scale_val and scale_val2 with format
* IIO_VAL_INT_PLUS_NANO, find the corresponding gain value and write it to the
* device.
*
* Must be called with lock held.
*/
static int pac1921_update_gain_from_scale(struct pac1921_priv *priv,
struct iio_chan_spec const *chan,
int scale_val, int scale_val2)
{
int ret;
switch (chan->channel) {
case PAC1921_CHAN_VBUS:
ret = pac1921_lookup_scale(pac1921_vbus_scales,
ARRAY_SIZE(pac1921_vbus_scales),
scale_val, scale_val2);
if (ret < 0)
return ret;
return pac1921_update_gain(priv, &priv->dv_gain, ret,
PAC1921_GAIN_DV_GAIN_MASK);
case PAC1921_CHAN_VSENSE:
ret = pac1921_lookup_scale(pac1921_vsense_scales,
ARRAY_SIZE(pac1921_vsense_scales),
scale_val, scale_val2);
if (ret < 0)
return ret;
return pac1921_update_gain(priv, &priv->di_gain, ret,
PAC1921_GAIN_DI_GAIN_MASK);
case PAC1921_CHAN_CURRENT:
ret = pac1921_lookup_scale(priv->current_scales,
ARRAY_SIZE(priv->current_scales),
scale_val, scale_val2);
if (ret < 0)
return ret;
return pac1921_update_gain(priv, &priv->di_gain, ret,
PAC1921_GAIN_DI_GAIN_MASK);
default:
return -EINVAL;
}
}
/*
* Retrieve the index of the given number of samples from the constant table.
* The returned index (if found) is the log2 of the given num_samples.
*/
static int pac1921_lookup_int_num_samples(int num_samples)
{
for (unsigned int i = 0; i < ARRAY_SIZE(pac1921_int_num_samples); i++)
if (pac1921_int_num_samples[i] == num_samples)
return i;
return -EINVAL;
}
/*
* Update the device with the given number of integration samples.
*
* Must be called with lock held.
*/
static int pac1921_update_int_num_samples(struct pac1921_priv *priv,
int num_samples)
{
unsigned int reg_val;
u8 n_samples;
int ret;
ret = pac1921_lookup_int_num_samples(num_samples);
if (ret < 0)
return ret;
n_samples = ret;
if (priv->n_samples == n_samples)
return 0;
reg_val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, n_samples);
ret = pac1921_update_cfg_reg(priv, PAC1921_REG_INT_CFG,
PAC1921_INT_CFG_SMPL_MASK, reg_val);
if (ret)
return ret;
priv->n_samples = n_samples;
priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples];
return 0;
}
static int pac1921_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int pac1921_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
guard(mutex)(&priv->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return pac1921_update_gain_from_scale(priv, chan, val, val2);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return pac1921_update_int_num_samples(priv, val);
default:
return -EINVAL;
}
}
static int pac1921_read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, char *label)
{
switch (chan->channel) {
case PAC1921_CHAN_VBUS:
return sprintf(label, "vbus\n");
case PAC1921_CHAN_VSENSE:
return sprintf(label, "vsense\n");
case PAC1921_CHAN_CURRENT:
return sprintf(label, "current\n");
case PAC1921_CHAN_POWER:
return sprintf(label, "power\n");
default:
return -EINVAL;
}
}
static int pac1921_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
guard(mutex)(&priv->lock);
switch (chan->channel) {
case PAC1921_CHAN_VBUS:
return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV);
case PAC1921_CHAN_VSENSE:
case PAC1921_CHAN_CURRENT:
return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV);
case PAC1921_CHAN_POWER:
return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV);
default:
return -EINVAL;
}
}
static int pac1921_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir, int state)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
u8 ovf_bit;
guard(mutex)(&priv->lock);
switch (chan->channel) {
case PAC1921_CHAN_VBUS:
ovf_bit = PAC1921_OVERFLOW_VBOV;
break;
case PAC1921_CHAN_VSENSE:
case PAC1921_CHAN_CURRENT:
ovf_bit = PAC1921_OVERFLOW_VSOV;
break;
case PAC1921_CHAN_POWER:
ovf_bit = PAC1921_OVERFLOW_VPOV;
break;
default:
return -EINVAL;
}
if (state)
priv->ovf_enabled_events |= ovf_bit;
else
priv->ovf_enabled_events &= ~ovf_bit;
return 0;
}
static int pac1921_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info, int *val,
int *val2)
{
switch (info) {
case IIO_EV_INFO_VALUE:
*val = PAC1921_RES_RESOLUTION;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_info pac1921_iio = {
.read_raw = pac1921_read_raw,
.read_avail = pac1921_read_avail,
.write_raw = pac1921_write_raw,
.write_raw_get_fmt = pac1921_write_raw_get_fmt,
.read_label = pac1921_read_label,
.read_event_config = pac1921_read_event_config,
.write_event_config = pac1921_write_event_config,
.read_event_value = pac1921_read_event_value,
};
static ssize_t pac1921_read_shunt_resistor(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
int vals[2];
if (chan->channel != PAC1921_CHAN_CURRENT)
return -EINVAL;
guard(mutex)(&priv->lock);
vals[0] = priv->rshunt_uohm;
vals[1] = MICRO;
return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals);
}
static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
u64 rshunt_uohm;
int val, val_fract;
int ret;
if (chan->channel != PAC1921_CHAN_CURRENT)
return -EINVAL;
ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
if (ret)
return ret;
rshunt_uohm = val * MICRO + val_fract;
if (rshunt_uohm == 0 || rshunt_uohm > INT_MAX)
return -EINVAL;
guard(mutex)(&priv->lock);
priv->rshunt_uohm = rshunt_uohm;
pac1921_calc_current_scales(priv);
return len;
}
/*
* Emit on sysfs the list of available scales contained in scales_tbl
*
* TODO:: this function can be replaced with iio_format_avail_list() if the
* latter will ever be exported.
*
* Must be called with lock held if the scales_tbl can change runtime (e.g. for
* the current scales table)
*/
static ssize_t pac1921_format_scale_avail(const int (*const scales_tbl)[2],
size_t size, char *buf)
{
ssize_t len = 0;
for (unsigned int i = 0; i < size; i++) {
if (i != 0) {
len += sysfs_emit_at(buf, len, " ");
if (len >= PAGE_SIZE)
return -EFBIG;
}
len += sysfs_emit_at(buf, len, "%d.%09d", scales_tbl[i][0],
scales_tbl[i][1]);
if (len >= PAGE_SIZE)
return -EFBIG;
}
len += sysfs_emit_at(buf, len, "\n");
return len;
}
/*
* Read available scales for a specific channel
*
* NOTE: using extended info insted of iio.read_avail() because access to
* current scales must be locked as they depend on shunt resistor which may
* change runtime. Caller of iio.read_avail() would access the table unlocked
* instead.
*/
static ssize_t pac1921_read_scale_avail(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct pac1921_priv *priv = iio_priv(indio_dev);
const int (*scales_tbl)[2];
size_t size;
switch (chan->channel) {
case PAC1921_CHAN_VBUS:
scales_tbl = pac1921_vbus_scales;
size = ARRAY_SIZE(pac1921_vbus_scales);
return pac1921_format_scale_avail(scales_tbl, size, buf);
case PAC1921_CHAN_VSENSE:
scales_tbl = pac1921_vsense_scales;
size = ARRAY_SIZE(pac1921_vsense_scales);
return pac1921_format_scale_avail(scales_tbl, size, buf);
case PAC1921_CHAN_CURRENT: {
guard(mutex)(&priv->lock);
scales_tbl = priv->current_scales;
size = ARRAY_SIZE(priv->current_scales);
return pac1921_format_scale_avail(scales_tbl, size, buf);
}
default:
return -EINVAL;
}
}
#define PAC1921_EXT_INFO_SCALE_AVAIL { \
.name = "scale_available", \
.read = pac1921_read_scale_avail, \
.shared = IIO_SEPARATE, \
}
static const struct iio_chan_spec_ext_info pac1921_ext_info_voltage[] = {
PAC1921_EXT_INFO_SCALE_AVAIL,
{}
};
static const struct iio_chan_spec_ext_info pac1921_ext_info_current[] = {
PAC1921_EXT_INFO_SCALE_AVAIL,
{
.name = "shunt_resistor",
.read = pac1921_read_shunt_resistor,
.write = pac1921_write_shunt_resistor,
.shared = IIO_SEPARATE,
},
{}
};
static const struct iio_event_spec pac1921_overflow_event[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec pac1921_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.channel = PAC1921_CHAN_VBUS,
.address = PAC1921_REG_VBUS,
.scan_index = PAC1921_CHAN_VBUS,
.scan_type = {
.sign = 'u',
.realbits = 10,
.storagebits = 16,
.endianness = IIO_CPU
},
.indexed = 1,
.event_spec = pac1921_overflow_event,
.num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
.ext_info = pac1921_ext_info_voltage,
},
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.channel = PAC1921_CHAN_VSENSE,
.address = PAC1921_REG_VSENSE,
.scan_index = PAC1921_CHAN_VSENSE,
.scan_type = {
.sign = 'u',
.realbits = 10,
.storagebits = 16,
.endianness = IIO_CPU
},
.indexed = 1,
.event_spec = pac1921_overflow_event,
.num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
.ext_info = pac1921_ext_info_voltage,
},
{
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.channel = PAC1921_CHAN_CURRENT,
.address = PAC1921_REG_VSENSE,
.scan_index = PAC1921_CHAN_CURRENT,
.scan_type = {
.sign = 'u',
.realbits = 10,
.storagebits = 16,
.endianness = IIO_CPU
},
.event_spec = pac1921_overflow_event,
.num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
.ext_info = pac1921_ext_info_current,
},
{
.type = IIO_POWER,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.channel = PAC1921_CHAN_POWER,
.address = PAC1921_REG_VPOWER,
.scan_index = PAC1921_CHAN_POWER,
.scan_type = {
.sign = 'u',
.realbits = 10,
.storagebits = 16,
.endianness = IIO_CPU
},
.event_spec = pac1921_overflow_event,
.num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
},
IIO_CHAN_SOFT_TIMESTAMP(PAC1921_NUM_MEAS_CHANS),
};
static irqreturn_t pac1921_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *idev = pf->indio_dev;
struct pac1921_priv *priv = iio_priv(idev);
int ret;
int bit;
int ch = 0;
guard(mutex)(&priv->lock);
if (!pac1921_data_ready(priv))
goto done;
ret = pac1921_check_push_overflow(idev, pf->timestamp);
if (ret)
goto done;
iio_for_each_active_channel(idev, bit) {
u16 val;
ret = pac1921_read_res(priv, idev->channels[ch].address, &val);
if (ret)
goto done;
priv->scan.chan[ch++] = val;
}
iio_push_to_buffers_with_timestamp(idev, &priv->scan, pf->timestamp);
done:
iio_trigger_notify_done(idev->trig);
return IRQ_HANDLED;
}
/*
* Initialize device by writing initial configuration and putting it into
* integration state.
*
* Must be called with lock held when called after first initialization
* (e.g. from pm resume)
*/
static int pac1921_init(struct pac1921_priv *priv)
{
unsigned int val;
int ret;
/* Enter READ state before configuration */
ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
PAC1921_INT_CFG_INTEN, 0);
if (ret)
return ret;
/* Configure gains, use 14-bits measurement resolution (HW default) */
val = FIELD_PREP(PAC1921_GAIN_DI_GAIN_MASK, priv->di_gain) |
FIELD_PREP(PAC1921_GAIN_DV_GAIN_MASK, priv->dv_gain);
ret = regmap_write(priv->regmap, PAC1921_REG_GAIN_CFG, val);
if (ret)
return ret;
/*
* Configure integration:
* - num of integration samples
* - filters enabled (HW default)
* - set READ/INT pin override (RIOV) to control operation mode via
* register instead of pin
*/
val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, priv->n_samples) |
PAC1921_INT_CFG_VSFEN | PAC1921_INT_CFG_VBFEN |
PAC1921_INT_CFG_RIOV;
ret = regmap_write(priv->regmap, PAC1921_REG_INT_CFG, val);
if (ret)
return ret;
/*
* Init control register:
* - VPower free run integration mode
* - OUT pin full scale range: 3V (HW default)
* - no timeout, no sleep, no sleep override, no recalc (HW defaults)
*/
val = FIELD_PREP(PAC1921_CONTROL_MXSL_MASK,
PAC1921_MXSL_VPOWER_FREE_RUN);
ret = regmap_write(priv->regmap, PAC1921_REG_CONTROL, val);
if (ret)
return ret;
/* Enable integration */
ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN);
if (ret)
return ret;
priv->first_integr_started = true;
priv->integr_started_time_jiffies = jiffies;
priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples];
return 0;
}
static int pac1921_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct pac1921_priv *priv = iio_priv(indio_dev);
int ret;
guard(mutex)(&priv->lock);
priv->first_integr_started = false;
priv->first_integr_done = false;
ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
PAC1921_INT_CFG_INTEN, 0);
if (ret)
return ret;
ret = regmap_update_bits(priv->regmap, PAC1921_REG_CONTROL,
PAC1921_CONTROL_SLEEP, PAC1921_CONTROL_SLEEP);
if (ret)
return ret;
return regulator_disable(priv->vdd);
}
static int pac1921_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct pac1921_priv *priv = iio_priv(indio_dev);
int ret;
guard(mutex)(&priv->lock);
ret = regulator_enable(priv->vdd);
if (ret)
return ret;
msleep(PAC1921_POWERUP_TIME_MS);
return pac1921_init(priv);
}
static DEFINE_SIMPLE_DEV_PM_OPS(pac1921_pm_ops, pac1921_suspend,
pac1921_resume);
static void pac1921_regulator_disable(void *data)
{
struct regulator *regulator = data;
regulator_disable(regulator);
}
static int pac1921_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pac1921_priv *priv;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
priv->client = client;
i2c_set_clientdata(client, indio_dev);
priv->regmap = devm_regmap_init_i2c(client, &pac1921_regmap_config);
if (IS_ERR(priv->regmap))
return dev_err_probe(dev, PTR_ERR(priv->regmap),
"Cannot initialize register map\n");
devm_mutex_init(dev, &priv->lock);
priv->dv_gain = PAC1921_DEFAULT_DV_GAIN;
priv->di_gain = PAC1921_DEFAULT_DI_GAIN;
priv->n_samples = PAC1921_DEFAULT_NUM_SAMPLES;
ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms",
&priv->rshunt_uohm);
if (ret)
return dev_err_probe(dev, ret,
"Cannot read shunt resistor property\n");
if (priv->rshunt_uohm == 0 || priv->rshunt_uohm > INT_MAX)
return dev_err_probe(dev, -EINVAL,
"Invalid shunt resistor: %u\n",
priv->rshunt_uohm);
pac1921_calc_current_scales(priv);
priv->vdd = devm_regulator_get(dev, "vdd");
if (IS_ERR(priv->vdd))
return dev_err_probe(dev, PTR_ERR(priv->vdd),
"Cannot get vdd regulator\n");
ret = regulator_enable(priv->vdd);
if (ret)
return dev_err_probe(dev, ret, "Cannot enable vdd regulator\n");
ret = devm_add_action_or_reset(dev, pac1921_regulator_disable,
priv->vdd);
if (ret)
return dev_err_probe(dev, ret,
"Cannot add action for vdd regulator disposal\n");
msleep(PAC1921_POWERUP_TIME_MS);
ret = pac1921_init(priv);
if (ret)
return dev_err_probe(dev, ret, "Cannot initialize device\n");
priv->iio_info = pac1921_iio;
indio_dev->name = "pac1921";
indio_dev->info = &priv->iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = pac1921_channels;
indio_dev->num_channels = ARRAY_SIZE(pac1921_channels);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&pac1921_trigger_handler, NULL);
if (ret)
return dev_err_probe(dev, ret,
"Cannot setup IIO triggered buffer\n");
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Cannot register IIO device\n");
return 0;
}
static const struct i2c_device_id pac1921_id[] = {
{ .name = "pac1921", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pac1921_id);
static const struct of_device_id pac1921_of_match[] = {
{ .compatible = "microchip,pac1921" },
{ }
};
MODULE_DEVICE_TABLE(of, pac1921_of_match);
static struct i2c_driver pac1921_driver = {
.driver = {
.name = "pac1921",
.pm = pm_sleep_ptr(&pac1921_pm_ops),
.of_match_table = pac1921_of_match,
},
.probe = pac1921_probe,
.id_table = pac1921_id,
};
module_i2c_driver(pac1921_driver);
MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>");
MODULE_DESCRIPTION("IIO driver for PAC1921 High-Side Power/Current Monitor");
MODULE_LICENSE("GPL");
|