1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com> 4 */ 5 6#include <dm.h> 7#include <log.h> 8#include <sysinfo.h> 9#include <asm/gpio.h> 10#include <dm/device_compat.h> 11 12/** 13 * struct sysinfo_gpio_priv - GPIO sysinfo private data 14 * @gpios: List of GPIOs used to detect the revision 15 * @gpio_num: The number of GPIOs in @gpios 16 * @revision: The revision as detected from the GPIOs. 17 */ 18struct sysinfo_gpio_priv { 19 struct gpio_desc *gpios; 20 int gpio_num, revision; 21}; 22 23static int sysinfo_gpio_detect(struct udevice *dev) 24{ 25 int ret; 26 struct sysinfo_gpio_priv *priv = dev_get_priv(dev); 27 28 ret = dm_gpio_get_values_as_int_base3(priv->gpios, priv->gpio_num); 29 if (ret < 0) 30 return ret; 31 32 priv->revision = ret; 33 return 0; 34} 35 36static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val) 37{ 38 struct sysinfo_gpio_priv *priv = dev_get_priv(dev); 39 40 switch (id) { 41 case SYSINFO_ID_BOARD_MODEL: 42 *val = priv->revision; 43 return 0; 44 default: 45 return -EINVAL; 46 }; 47} 48 49static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val) 50{ 51 struct sysinfo_gpio_priv *priv = dev_get_priv(dev); 52 53 switch (id) { 54 case SYSINFO_ID_BOARD_MODEL: { 55 const char *name = NULL; 56 int i, ret; 57 u32 revision; 58 59 for (i = 0; ; i++) { 60 ret = dev_read_u32_index(dev, "revisions", i, 61 &revision); 62 if (ret) { 63 if (ret != -EOVERFLOW) 64 return ret; 65 break; 66 } 67 68 if (revision == priv->revision) { 69 ret = dev_read_string_index(dev, "names", i, 70 &name); 71 if (ret < 0) 72 return ret; 73 break; 74 } 75 } 76 if (!name) 77 name = "unknown"; 78 79 strncpy(val, name, size); 80 val[size - 1] = '\0'; 81 return 0; 82 } 83 default: 84 return -EINVAL; 85 }; 86} 87 88static const struct sysinfo_ops sysinfo_gpio_ops = { 89 .detect = sysinfo_gpio_detect, 90 .get_int = sysinfo_gpio_get_int, 91 .get_str = sysinfo_gpio_get_str, 92}; 93 94static int sysinfo_gpio_probe(struct udevice *dev) 95{ 96 int ret; 97 struct sysinfo_gpio_priv *priv = dev_get_priv(dev); 98 99 priv->gpio_num = gpio_get_list_count(dev, "gpios"); 100 if (priv->gpio_num < 0) { 101 dev_err(dev, "could not get gpios length (err = %d)\n", 102 priv->gpio_num); 103 return priv->gpio_num; 104 } 105 106 priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios)); 107 if (!priv->gpios) { 108 dev_err(dev, "could not allocate memory for %d gpios\n", 109 priv->gpio_num); 110 return -ENOMEM; 111 } 112 113 ret = gpio_request_list_by_name(dev, "gpios", priv->gpios, 114 priv->gpio_num, GPIOD_IS_IN); 115 if (ret != priv->gpio_num) { 116 dev_err(dev, "could not get gpios (err = %d)\n", 117 priv->gpio_num); 118 return ret; 119 } 120 121 if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) { 122 dev_err(dev, "revisions or names properties missing\n"); 123 return -ENOENT; 124 } 125 126 return 0; 127} 128 129static const struct udevice_id sysinfo_gpio_ids[] = { 130 { .compatible = "gpio-sysinfo" }, 131 { /* sentinel */ } 132}; 133 134U_BOOT_DRIVER(sysinfo_gpio) = { 135 .name = "sysinfo_gpio", 136 .id = UCLASS_SYSINFO, 137 .of_match = sysinfo_gpio_ids, 138 .ops = &sysinfo_gpio_ops, 139 .priv_auto = sizeof(struct sysinfo_gpio_priv), 140 .probe = sysinfo_gpio_probe, 141}; 142