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