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