1 /*
2 * (C) Copyright 2020 Rockchip Electronics Co., Ltd
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7 #include <common.h>
8 #include <adc.h>
9 #include <div64.h>
10 #include <fdtdec.h>
11 #include <dm/uclass.h>
12
13 DECLARE_GLOBAL_DATA_PTR;
14
adc_raw_to_mV(struct udevice * dev,unsigned int raw,int * mV)15 static int adc_raw_to_mV(struct udevice *dev, unsigned int raw, int *mV)
16 {
17 unsigned int data_mask;
18 int ret, vref = 1800000;
19 u64 raw64 = raw;
20
21 ret = adc_data_mask(dev, &data_mask);
22 if (ret)
23 return ret;
24
25 raw64 *= vref;
26 do_div(raw64, data_mask);
27 *mV = raw64;
28
29 return 0;
30 }
31
key_read(int code)32 int key_read(int code)
33 {
34 const void *fdt_blob = gd->fdt_blob;
35 struct udevice *dev;
36 int adc_node, offset;
37 int t, down_threshold = -1, up_threshold;
38 int ret, num = 0, volt_margin = 150000; /* will be div 2 */
39 int mV, cd, voltage = -1;
40 int min, max;
41 u32 chn[2], adc;
42
43 ret = uclass_get_device_by_name(UCLASS_ADC, "saradc", &dev);
44 if (ret) {
45 debug("No saradc device, ret=%d\n", ret);
46 return 0;
47 }
48
49 adc_node = fdt_node_offset_by_compatible(fdt_blob, 0, "adc-keys");
50 if (adc_node < 0) {
51 debug("No 'adc-keys' node, ret=%d\n", adc_node);
52 return 0;
53 }
54
55 ret = fdtdec_get_int_array(fdt_blob, adc_node, "io-channels",
56 chn, ARRAY_SIZE(chn));
57 if (ret) {
58 debug("Can't read 'io-channels', ret=%d\n", ret);
59 return 0;
60 }
61
62 up_threshold = fdtdec_get_int(fdt_blob, adc_node,
63 "keyup-threshold-microvolt", -ENODATA);
64 if (up_threshold < 0) {
65 debug("Can't read 'keyup-threshold-microvolt'\n");
66 return 0;
67 }
68
69 /* find the expected key-code */
70 for (offset = fdt_first_subnode(fdt_blob, adc_node);
71 offset >= 0;
72 offset = fdt_next_subnode(fdt_blob, offset)) {
73 cd = fdtdec_get_int(fdt_blob, offset, "linux,code", -ENODATA);
74 if (cd < 0) {
75 debug("Can't read 'linux,code', ret=%d\n", cd);
76 return 0;
77 }
78
79 if (cd == code) {
80 voltage = fdtdec_get_int(fdt_blob, offset,
81 "press-threshold-microvolt", -ENODATA);
82 if (voltage < 0) {
83 debug("Can't read 'press-threshold-microvolt'\n");
84 return 0;
85 }
86 break;
87 }
88 }
89
90 if (voltage < 0)
91 return 0;
92
93 for (offset = fdt_first_subnode(fdt_blob, adc_node);
94 offset >= 0;
95 offset = fdt_next_subnode(fdt_blob, offset)) {
96 t = fdtdec_get_int(fdt_blob, offset,
97 "press-threshold-microvolt", -ENODATA);
98 if (t < 0) {
99 debug("Can't read 'press-threshold-microvolt'\n");
100 return 0;
101 }
102
103 if (t > voltage && t < up_threshold)
104 up_threshold = t;
105 else if (t < voltage && t > down_threshold)
106 down_threshold = t;
107 num++;
108 }
109
110 /* although one node only, it doesn't mean only one key on hardware */
111 if (num == 1) {
112 down_threshold = voltage - volt_margin;
113 up_threshold = voltage + volt_margin;
114 }
115
116 /*
117 * Define the voltage range such that the button is only pressed
118 * when the voltage is closest to its own press-threshold-microvolt
119 */
120 if (down_threshold < 0)
121 min = 0;
122 else
123 min = down_threshold + (voltage - down_threshold) / 2;
124
125 max = voltage + (up_threshold - voltage) / 2;
126
127 /* now, read key status */
128 ret = adc_channel_single_shot("saradc", chn[1], &adc);
129 if (ret) {
130 debug("Failed to read adc%d, ret=%d\n", chn[1], ret);
131 return 0;
132 }
133
134 ret = adc_raw_to_mV(dev, adc, &mV);
135 if (ret) {
136 debug("Failed to convert adc to mV, ret=%d\n", ret);
137 return 0;
138 }
139
140 debug("key[%d] <%d, %d, %d>: adc=%d -> mV=%d\n",
141 code, min, voltage, max, adc, mV);
142
143 return (mV <= max && mV >= min);
144 }
145
146