/* * (C) Copyright 2021 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #define is_digit(c) ((c) >= '0' && (c) <= '9') #define is_abcd(c) ((c) >= 'a' && (c) <= 'd') #define is_equal(c) ((c) == '=') #define KEY_WORDS_ADC_CTRL "#_" #define KEY_WORDS_ADC_CH "_ch" #define KEY_WORDS_GPIO "#gpio" #define MAX_ADC_CH_NR 10 #define MAX_GPIO_NR 10 static fdt_addr_t gpio_base_addr[MAX_GPIO_NR]; static uint32_t gpio_record[MAX_GPIO_NR]; static int adc_record[MAX_ADC_CH_NR]; #ifdef CONFIG_ROCKCHIP_GPIO_V2 #define GPIO_SWPORT_DDR 0x08 #define GPIO_EXT_PORT 0x70 #define WMSK_SETBIT(n) (n << 16 | n) #define WMSK_CLRBIT(n) (n << 16) #define REG_PLUS4(off, n) (off + (n >= BIT(16) ? 4 : 0)) #define BIT_SUB16(n) (n >= BIT(16) ? (n >> 16) : n) static int gpio_read(fdt_addr_t gpio_addr, int gpio_bank, int gpio_pin) { uint32_t off, bit; bit = gpio_bank * 8 + gpio_pin; off = REG_PLUS4(GPIO_SWPORT_DDR, bit); bit = BIT_SUB16(bit); writel(WMSK_CLRBIT(bit), gpio_addr + off); return readl(gpio_addr + GPIO_EXT_PORT); } #else #define GPIO_SWPORT_DDR 0x04 #define GPIO_EXT_PORT 0x50 static int gpio_read(fdt_addr_t gpio_addr, int gpio_bank, int gpio_pin) { uint32_t val; val = readl(gpio_addr + GPIO_SWPORT_DDR); val &= ~(1 << (gpio_bank * 8 + gpio_pin)); writel(val, gpio_addr + GPIO_SWPORT_DDR); return readl(gpio_addr + GPIO_EXT_PORT); } #endif static int gpio_parse_base_address(fdt_addr_t *gpio_base_addr) { static int initialized; ofnode parent, node; int idx = 0; if (initialized) return 0; parent = ofnode_path("/pinctrl"); if (!ofnode_valid(parent)) { debug(" - No pinctrl node\n"); return -EINVAL; } ofnode_for_each_subnode(node, parent) { if (!ofnode_get_property(node, "gpio-controller", NULL)) { debug(" - No gpio controller node\n"); continue; } gpio_base_addr[idx] = ofnode_get_addr(node); debug(" - gpio%d: 0x%x\n", idx, (uint32_t)gpio_base_addr[idx]); idx++; } if (idx == 0) { debug(" - parse gpio address failed\n"); return -EINVAL; } initialized = 1; return 0; } static void hwid_init_data(void) { memset(adc_record, 0, sizeof(adc_record)); memset(gpio_record, 0, sizeof(gpio_record)); memset(gpio_base_addr, 0, sizeof(gpio_base_addr)); } /* * How to use ? * * 1. Pack dtbs into resource.img, dtb file name: * - End with: ".dtb" * - Pattern: ...#_[controller]_ch[channel]=[value]...dtb * @controller: adc controller name in dts, eg. "saradc", ...; * @channel: adc channel; * @value: adc value; * - Eg: ...#_saradc_ch1=223#_saradc_ch2=650....dtb * * 2. U-Boot dtsi about adc node: * - Add "u-boot,dm-pre-reloc;" * - Set node "okay" */ static int hwid_adc_find_dtb(const char *file_name) { char *cell_name, *adc_tail, *adc_head, *p; int prefix_len, chn_len, len; int found = 0, margin = 30; int ret, channel; char dev_name[32]; char adc_val[10]; ulong dtb_adc; u32 raw_adc; debug("[HW-ADC]: %s\n", file_name); chn_len = strlen(KEY_WORDS_ADC_CH); prefix_len = strlen(KEY_WORDS_ADC_CTRL); cell_name = strstr(file_name, KEY_WORDS_ADC_CTRL); while (cell_name) { /* Parse adc controller name */ adc_tail = strstr(cell_name, KEY_WORDS_ADC_CH); adc_head = cell_name + prefix_len; len = adc_tail - adc_head; strlcpy(dev_name, adc_head, len + 1); /* Parse adc channel */ p = adc_tail + chn_len; if (is_digit(*p) && is_equal(*(p + 1))) { channel = *p - '0'; } else { debug(" - invalid format: %s\n", cell_name); return 0; } /* * Read raw adc value * * It doesn't need to read adc value every loop, reading once * is enough. We use adc_record[] to save what we have read, zero * means not read before. */ if (adc_record[channel] == 0) { ret = adc_channel_single_shot(dev_name, channel, &raw_adc); if (ret) { debug(" - failed to read adc, ret=%d\n", ret); return 0; } adc_record[channel] = raw_adc; } /* Parse dtb adc value */ p = adc_tail + chn_len + 2; /* 2: channel and '=' */ while (*p && is_digit(*p)) { len++; p++; } strlcpy(adc_val, adc_tail + chn_len + 2, len + 1); dtb_adc = simple_strtoul(adc_val, NULL, 10); found = (abs(dtb_adc - adc_record[channel]) <= margin) ? 1 : 0; debug(" - dev=%s, channel=%d, dtb_adc=%ld, read=%d, found=%d\n", dev_name, channel, dtb_adc, adc_record[channel], found); if (!found) break; cell_name = strstr(p, KEY_WORDS_ADC_CTRL); } return found; } /* * How to use ? * * 1. Pack dtbs into resource.img, dtb file name: * - End with: ".dtb" * - Pattern: ...#gpio[pin]=[level]...dtb * @pin: gpio name, eg. 0a2 means GPIO0A2 * @level: gpio level, 0 or 1 * - Eg: ...#gpio0a6=1#gpio1c2=0....dtb * * 2. U-Boot dtsi about gpio node: * - Add "u-boot,dm-pre-reloc;" for [all] gpio node; * - Set all gpio node "disabled" (We just want their property); */ static int hwid_gpio_find_dtb(const char *file_name) { uint8_t port, pin, bank, lvl, val; char *cell_name, *p; int ret, prefix_len; int found = 0; u32 bit; debug("[HW-GPIO]: %s\n", file_name); if (gpio_base_addr[0] == 0) { ret = gpio_parse_base_address(gpio_base_addr); if (ret) { debug("[HW-GPIO]: Can't parse gpio base, ret=%d\n", ret); return 0; } } prefix_len = strlen(KEY_WORDS_GPIO); cell_name = strstr(file_name, KEY_WORDS_GPIO); while (cell_name) { p = cell_name + prefix_len; /* Invalid format ? */ if (!(is_digit(*(p + 0)) && is_abcd(*(p + 1)) && is_digit(*(p + 2)) && is_equal(*(p + 3)) && is_digit(*(p + 4)))) { debug(" - invalid format: %s\n", cell_name); return 0; } /* Read gpio value */ port = *(p + 0) - '0'; bank = *(p + 1) - 'a'; pin = *(p + 2) - '0'; lvl = *(p + 4) - '0'; /* * It doesn't need to read gpio value every loop, reading once * is enough. We use gpio_record[] to save what we have read, zero * means not read before. */ if (gpio_record[port] == 0) { if (!gpio_base_addr[port]) { debug(" - can't find gpio%d base\n", port); return 0; } gpio_record[port] = gpio_read(gpio_base_addr[port], bank, pin); } /* Verify result */ bit = bank * 8 + pin; val = gpio_record[port] & (1 << bit) ? 1 : 0; found = (val == !!lvl) ? 1 : 0; debug(" - gpio%d%c%d=%d, read=%d, found=%d\n", port, bank + 'a', pin, lvl, val, found); if (!found) break; cell_name = strstr(p, KEY_WORDS_GPIO); } return found; } struct resource_file *resource_read_hwid_dtb(void) { struct resource_file *file; struct list_head *node; hwid_init_data(); list_for_each(node, &entry_head) { file = list_entry(node, struct resource_file, link); if (!strstr(file->name, DTB_SUFFIX)) continue; if (strstr(file->name, KEY_WORDS_ADC_CTRL) && strstr(file->name, KEY_WORDS_ADC_CH) && hwid_adc_find_dtb(file->name)) { return file; } else if (strstr(file->name, KEY_WORDS_GPIO) && hwid_gpio_find_dtb(file->name)) { return file; } } return NULL; }