1 /*
2 * (C) Copyright 2021 Rockchip Electronics Co., Ltd
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6 #include <common.h>
7 #include <adc.h>
8 #include <asm/io.h>
9 #include <dm/ofnode.h>
10 #include <asm/arch/resource_img.h>
11
12 #define is_digit(c) ((c) >= '0' && (c) <= '9')
13 #define is_abcd(c) ((c) >= 'a' && (c) <= 'd')
14 #define is_equal(c) ((c) == '=')
15 #define KEY_WORDS_ADC_CTRL "#_"
16 #define KEY_WORDS_ADC_CH "_ch"
17 #define KEY_WORDS_GPIO "#gpio"
18 #define MAX_ADC_CH_NR 10
19 #define MAX_GPIO_NR 10
20
21 static fdt_addr_t gpio_base_addr[MAX_GPIO_NR];
22 static uint32_t gpio_record[MAX_GPIO_NR];
23 static int adc_record[MAX_ADC_CH_NR];
24
25 #ifdef CONFIG_ROCKCHIP_GPIO_V2
26 #define GPIO_SWPORT_DDR 0x08
27 #define GPIO_EXT_PORT 0x70
28 #define WMSK_SETBIT(n) (n << 16 | n)
29 #define WMSK_CLRBIT(n) (n << 16)
30 #define REG_PLUS4(off, n) (off + (n >= BIT(16) ? 4 : 0))
31 #define BIT_SUB16(n) (n >= BIT(16) ? (n >> 16) : n)
gpio_read(fdt_addr_t gpio_addr,int gpio_bank,int gpio_pin)32 static int gpio_read(fdt_addr_t gpio_addr, int gpio_bank, int gpio_pin)
33 {
34 uint32_t off, bit;
35
36 bit = gpio_bank * 8 + gpio_pin;
37 off = REG_PLUS4(GPIO_SWPORT_DDR, bit);
38 bit = BIT_SUB16(bit);
39 writel(WMSK_CLRBIT(bit), gpio_addr + off);
40
41 return readl(gpio_addr + GPIO_EXT_PORT);
42 }
43
44 #else
45 #define GPIO_SWPORT_DDR 0x04
46 #define GPIO_EXT_PORT 0x50
gpio_read(fdt_addr_t gpio_addr,int gpio_bank,int gpio_pin)47 static int gpio_read(fdt_addr_t gpio_addr, int gpio_bank, int gpio_pin)
48 {
49 uint32_t val;
50
51 val = readl(gpio_addr + GPIO_SWPORT_DDR);
52 val &= ~(1 << (gpio_bank * 8 + gpio_pin));
53 writel(val, gpio_addr + GPIO_SWPORT_DDR);
54
55 return readl(gpio_addr + GPIO_EXT_PORT);
56 }
57 #endif
58
gpio_parse_base_address(fdt_addr_t * gpio_base_addr)59 static int gpio_parse_base_address(fdt_addr_t *gpio_base_addr)
60 {
61 static int initialized;
62 ofnode parent, node;
63 int idx = 0;
64
65 if (initialized)
66 return 0;
67
68 parent = ofnode_path("/pinctrl");
69 if (!ofnode_valid(parent)) {
70 debug(" - No pinctrl node\n");
71 return -EINVAL;
72 }
73
74 ofnode_for_each_subnode(node, parent) {
75 if (!ofnode_get_property(node, "gpio-controller", NULL)) {
76 debug(" - No gpio controller node\n");
77 continue;
78 }
79 gpio_base_addr[idx] = ofnode_get_addr(node);
80 debug(" - gpio%d: 0x%x\n", idx, (uint32_t)gpio_base_addr[idx]);
81 idx++;
82 }
83
84 if (idx == 0) {
85 debug(" - parse gpio address failed\n");
86 return -EINVAL;
87 }
88
89 initialized = 1;
90
91 return 0;
92 }
93
hwid_init_data(void)94 static void hwid_init_data(void)
95 {
96 memset(adc_record, 0, sizeof(adc_record));
97 memset(gpio_record, 0, sizeof(gpio_record));
98 memset(gpio_base_addr, 0, sizeof(gpio_base_addr));
99 }
100
101 /*
102 * How to use ?
103 *
104 * 1. Pack dtbs into resource.img, dtb file name:
105 * - End with: ".dtb"
106 * - Pattern: ...#_[controller]_ch[channel]=[value]...dtb
107 * @controller: adc controller name in dts, eg. "saradc", ...;
108 * @channel: adc channel;
109 * @value: adc value;
110 * - Eg: ...#_saradc_ch1=223#_saradc_ch2=650....dtb
111 *
112 * 2. U-Boot dtsi about adc node:
113 * - Add "u-boot,dm-pre-reloc;"
114 * - Set node "okay"
115 */
hwid_adc_find_dtb(const char * file_name)116 static int hwid_adc_find_dtb(const char *file_name)
117 {
118 char *cell_name, *adc_tail, *adc_head, *p;
119 int prefix_len, chn_len, len;
120 int found = 0, margin = 30;
121 int ret, channel;
122 char dev_name[32];
123 char adc_val[10];
124 ulong dtb_adc;
125 u32 raw_adc;
126
127 debug("[HW-ADC]: %s\n", file_name);
128
129 chn_len = strlen(KEY_WORDS_ADC_CH);
130 prefix_len = strlen(KEY_WORDS_ADC_CTRL);
131 cell_name = strstr(file_name, KEY_WORDS_ADC_CTRL);
132 while (cell_name) {
133 /* Parse adc controller name */
134 adc_tail = strstr(cell_name, KEY_WORDS_ADC_CH);
135 adc_head = cell_name + prefix_len;
136 len = adc_tail - adc_head;
137 strlcpy(dev_name, adc_head, len + 1);
138
139 /* Parse adc channel */
140 p = adc_tail + chn_len;
141 if (is_digit(*p) && is_equal(*(p + 1))) {
142 channel = *p - '0';
143 } else {
144 debug(" - invalid format: %s\n", cell_name);
145 return 0;
146 }
147
148 /*
149 * Read raw adc value
150 *
151 * It doesn't need to read adc value every loop, reading once
152 * is enough. We use adc_record[] to save what we have read, zero
153 * means not read before.
154 */
155 if (adc_record[channel] == 0) {
156 ret = adc_channel_single_shot(dev_name, channel, &raw_adc);
157 if (ret) {
158 debug(" - failed to read adc, ret=%d\n", ret);
159 return 0;
160 }
161 adc_record[channel] = raw_adc;
162 }
163
164 /* Parse dtb adc value */
165 p = adc_tail + chn_len + 2; /* 2: channel and '=' */
166 while (*p && is_digit(*p)) {
167 len++;
168 p++;
169 }
170 strlcpy(adc_val, adc_tail + chn_len + 2, len + 1);
171 dtb_adc = simple_strtoul(adc_val, NULL, 10);
172 found = (abs(dtb_adc - adc_record[channel]) <= margin) ? 1 : 0;
173 debug(" - dev=%s, channel=%d, dtb_adc=%ld, read=%d, found=%d\n",
174 dev_name, channel, dtb_adc, adc_record[channel], found);
175 if (!found)
176 break;
177 cell_name = strstr(p, KEY_WORDS_ADC_CTRL);
178
179 }
180
181 return found;
182 }
183
184 /*
185 * How to use ?
186 *
187 * 1. Pack dtbs into resource.img, dtb file name:
188 * - End with: ".dtb"
189 * - Pattern: ...#gpio[pin]=[level]...dtb
190 * @pin: gpio name, eg. 0a2 means GPIO0A2
191 * @level: gpio level, 0 or 1
192 * - Eg: ...#gpio0a6=1#gpio1c2=0....dtb
193 *
194 * 2. U-Boot dtsi about gpio node:
195 * - Add "u-boot,dm-pre-reloc;" for [all] gpio node;
196 * - Set all gpio node "disabled" (We just want their property);
197 */
hwid_gpio_find_dtb(const char * file_name)198 static int hwid_gpio_find_dtb(const char *file_name)
199 {
200 uint8_t port, pin, bank, lvl, val;
201 char *cell_name, *p;
202 int ret, prefix_len;
203 int found = 0;
204 u32 bit;
205
206 debug("[HW-GPIO]: %s\n", file_name);
207
208 if (gpio_base_addr[0] == 0) {
209 ret = gpio_parse_base_address(gpio_base_addr);
210 if (ret) {
211 debug("[HW-GPIO]: Can't parse gpio base, ret=%d\n", ret);
212 return 0;
213 }
214 }
215
216 prefix_len = strlen(KEY_WORDS_GPIO);
217 cell_name = strstr(file_name, KEY_WORDS_GPIO);
218 while (cell_name) {
219 p = cell_name + prefix_len;
220
221 /* Invalid format ? */
222 if (!(is_digit(*(p + 0)) && is_abcd(*(p + 1)) &&
223 is_digit(*(p + 2)) && is_equal(*(p + 3)) &&
224 is_digit(*(p + 4)))) {
225 debug(" - invalid format: %s\n", cell_name);
226 return 0;
227 }
228
229 /* Read gpio value */
230 port = *(p + 0) - '0';
231 bank = *(p + 1) - 'a';
232 pin = *(p + 2) - '0';
233 lvl = *(p + 4) - '0';
234
235 /*
236 * It doesn't need to read gpio value every loop, reading once
237 * is enough. We use gpio_record[] to save what we have read, zero
238 * means not read before.
239 */
240 if (gpio_record[port] == 0) {
241 if (!gpio_base_addr[port]) {
242 debug(" - can't find gpio%d base\n", port);
243 return 0;
244 }
245 gpio_record[port] =
246 gpio_read(gpio_base_addr[port], bank, pin);
247 }
248
249 /* Verify result */
250 bit = bank * 8 + pin;
251 val = gpio_record[port] & (1 << bit) ? 1 : 0;
252 found = (val == !!lvl) ? 1 : 0;
253 debug(" - gpio%d%c%d=%d, read=%d, found=%d\n",
254 port, bank + 'a', pin, lvl, val, found);
255 if (!found)
256 break;
257 cell_name = strstr(p, KEY_WORDS_GPIO);
258 }
259
260 return found;
261 }
262
resource_read_hwid_dtb(void)263 struct resource_file *resource_read_hwid_dtb(void)
264 {
265 struct resource_file *file;
266 struct list_head *node;
267
268 hwid_init_data();
269
270 list_for_each(node, &entry_head) {
271 file = list_entry(node, struct resource_file, link);
272 if (!strstr(file->name, DTB_SUFFIX))
273 continue;
274
275 if (strstr(file->name, KEY_WORDS_ADC_CTRL) &&
276 strstr(file->name, KEY_WORDS_ADC_CH) &&
277 hwid_adc_find_dtb(file->name)) {
278 return file;
279 } else if (strstr(file->name, KEY_WORDS_GPIO) &&
280 hwid_gpio_find_dtb(file->name)) {
281 return file;
282 }
283 }
284
285 return NULL;
286 }
287
288