You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
7.0 KiB
288 lines
7.0 KiB
/*
|
|
* (C) Copyright 2021 Rockchip Electronics Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#include <common.h>
|
|
#include <adc.h>
|
|
#include <asm/io.h>
|
|
#include <dm/ofnode.h>
|
|
#include <asm/arch/resource_img.h>
|
|
|
|
#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;
|
|
}
|
|
|