1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2019 Google LLC
4 */
5
6#define LOG_CATEGORY	UCLASS_SYSINFO
7
8#include <common.h>
9#include <bloblist.h>
10#include <command.h>
11#include <cros_ec.h>
12#include <dm.h>
13#include <event.h>
14#include <init.h>
15#include <log.h>
16#include <sysinfo.h>
17#include <acpi/acpigen.h>
18#include <asm-generic/gpio.h>
19#include <asm/acpi_nhlt.h>
20#include <asm/cb_sysinfo.h>
21#include <asm/intel_gnvs.h>
22#include <asm/intel_pinctrl.h>
23#include <dm/acpi.h>
24#include <linux/delay.h>
25#include "variant_gpio.h"
26
27DECLARE_GLOBAL_DATA_PTR;
28
29struct cros_gpio_info {
30	const char *linux_name;
31	enum cros_gpio_t type;
32	int gpio_num;
33	int flags;
34};
35
36static int coral_check_ll_boot(void)
37{
38	if (!ll_boot_init()) {
39		printf("Running as secondary loader");
40		if (CONFIG_IS_ENABLED(COREBOOT_SYSINFO) &&
41		    gd->arch.coreboot_table) {
42			int ret;
43
44			printf(" (found coreboot table at %lx)",
45			       gd->arch.coreboot_table);
46
47			ret = get_coreboot_info(&lib_sysinfo);
48			if (ret) {
49				printf("\nFailed to parse coreboot tables (err=%d)\n",
50				       ret);
51				return ret;
52			}
53		}
54
55		printf("\n");
56	}
57
58	return 0;
59}
60EVENT_SPY_SIMPLE(EVT_MISC_INIT_F, coral_check_ll_boot);
61
62int arch_misc_init(void)
63{
64	return 0;
65}
66
67static int get_memconfig(struct udevice *dev)
68{
69	struct gpio_desc gpios[4];
70	int cfg;
71	int ret;
72
73	ret = gpio_request_list_by_name(dev, "memconfig-gpios", gpios,
74					ARRAY_SIZE(gpios),
75					GPIOD_IS_IN | GPIOD_PULL_UP);
76	if (ret < 0) {
77		log_debug("Cannot get GPIO list '%s' (%d)\n", dev->name, ret);
78		return ret;
79	}
80
81	/* Give the lines time to settle */
82	udelay(10);
83
84	ret = dm_gpio_get_values_as_int(gpios, ARRAY_SIZE(gpios));
85	if (ret < 0)
86		return log_msg_ret("get", ret);
87	cfg = ret;
88
89	ret = gpio_free_list(dev, gpios, ARRAY_SIZE(gpios));
90	if (ret)
91		return log_msg_ret("free", ret);
92
93	return cfg;
94}
95
96/**
97 * get_skuconfig() - Get the SKU number either from pins or the EC
98 *
99 * Two options are supported:
100 *     skuconfig-gpios - two pins in the device tree (tried first)
101 *     EC              - reading from the EC (backup)
102 *
103 * @dev: sysinfo device to use
104 * Return: SKU ID, or -ve error if not found
105 */
106static int get_skuconfig(struct udevice *dev)
107{
108	struct gpio_desc gpios[2];
109	int cfg;
110	int ret;
111
112	ret = gpio_request_list_by_name(dev, "skuconfig-gpios", gpios,
113					ARRAY_SIZE(gpios),
114					GPIOD_IS_IN);
115	if (ret != ARRAY_SIZE(gpios)) {
116		struct udevice *cros_ec;
117
118		log_debug("Cannot get GPIO list '%s' (%d)\n", dev->name, ret);
119
120		/* Try the EC */
121		ret = uclass_first_device_err(UCLASS_CROS_EC, &cros_ec);
122		if (ret < 0) {
123			log_err("Cannot find EC for SKU details\n");
124			return log_msg_ret("sku", ret);
125		}
126		ret = cros_ec_get_sku_id(cros_ec);
127		if (ret < 0) {
128			log_err("Cannot read SKU details\n");
129			return log_msg_ret("sku", ret);
130		}
131
132		return ret;
133	}
134
135	ret = dm_gpio_get_values_as_int_base3(gpios, ARRAY_SIZE(gpios));
136	if (ret < 0)
137		return log_msg_ret("get", ret);
138	cfg = ret;
139
140	ret = gpio_free_list(dev, gpios, ARRAY_SIZE(gpios));
141	if (ret)
142		return log_msg_ret("free", ret);
143
144	return cfg;
145}
146
147static int coral_get_str(struct udevice *dev, int id, size_t size, char *val)
148{
149	int ret;
150
151	if (IS_ENABLED(CONFIG_SPL_BUILD))
152		return -ENOSYS;
153
154	switch (id) {
155	case SYSINFO_ID_SMBIOS_SYSTEM_VERSION:
156	case SYSINFO_ID_SMBIOS_BASEBOARD_VERSION: {
157		ret = get_skuconfig(dev);
158
159		if (ret < 0)
160			return ret;
161		if (size < 15)
162			return -ENOSPC;
163		sprintf(val, "rev%d", ret);
164		break;
165	}
166	case SYSINFO_ID_BOARD_MODEL: {
167		int mem_config, sku_config;
168		const char *model;
169
170		ret = get_memconfig(dev);
171		if (ret < 0)
172			log_warning("Unable to read memconfig (err=%d)\n", ret);
173		mem_config = ret;
174		ret = get_skuconfig(dev);
175		if (ret < 0)
176			log_warning("Unable to read skuconfig (err=%d)\n", ret);
177		sku_config = ret;
178		model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
179		snprintf(val, size, "%s (memconfig %d, SKU %d)", model,
180			 mem_config, sku_config);
181		break;
182	}
183	default:
184		return -ENOENT;
185	}
186
187	return 0;
188}
189
190int chromeos_get_gpio(const struct udevice *dev, const char *prop,
191		      enum cros_gpio_t type, struct cros_gpio_info *info)
192{
193	struct udevice *pinctrl;
194	struct gpio_desc desc;
195	int ret;
196
197	ret = gpio_request_by_name((struct udevice *)dev, prop, 0, &desc, 0);
198	if (ret == -ENOTBLK) {
199		info->gpio_num = CROS_GPIO_VIRTUAL;
200		log_debug("GPIO '%s' is virtual\n", prop);
201	} else if (ret) {
202		return log_msg_ret("gpio", ret);
203	} else {
204		info->gpio_num = desc.offset;
205		dm_gpio_free((struct udevice *)dev, &desc);
206	}
207	info->linux_name = dev_read_string(desc.dev, "linux-name");
208	if (!info->linux_name)
209		return log_msg_ret("linux-name", -ENOENT);
210	info->type = type;
211	/* Get ACPI pin from GPIO library if available */
212	if (info->gpio_num != CROS_GPIO_VIRTUAL) {
213		pinctrl = dev_get_parent(desc.dev);
214		info->gpio_num = intel_pinctrl_get_acpi_pin(pinctrl,
215							    info->gpio_num);
216	}
217	info->flags = desc.flags & GPIOD_ACTIVE_LOW ? CROS_GPIO_ACTIVE_LOW :
218		CROS_GPIO_ACTIVE_HIGH;
219	if (!ret)
220		dm_gpio_free(desc.dev, &desc);
221
222	return 0;
223}
224
225static int chromeos_acpi_gpio_generate(const struct udevice *dev,
226				       struct acpi_ctx *ctx)
227{
228	struct cros_gpio_info info[3];
229	int count, i;
230	int ret;
231
232	count = 3;
233	ret = chromeos_get_gpio(dev, "recovery-gpios", CROS_GPIO_REC, &info[0]);
234	if (ret)
235		return log_msg_ret("rec", ret);
236	ret = chromeos_get_gpio(dev, "write-protect-gpios", CROS_GPIO_WP,
237				&info[1]);
238	if (ret)
239		return log_msg_ret("wp", ret);
240	ret = chromeos_get_gpio(dev, "phase-enforce-gpios", CROS_GPIO_PE,
241				&info[2]);
242	if (ret)
243		return log_msg_ret("phase", ret);
244	acpigen_write_scope(ctx, "\\");
245	acpigen_write_name(ctx, "OIPG");
246	acpigen_write_package(ctx, count);
247	for (i = 0; i < count; i++) {
248		acpigen_write_package(ctx, 4);
249		acpigen_write_integer(ctx, info[i].type);
250		acpigen_write_integer(ctx, info[i].flags);
251		acpigen_write_integer(ctx, info[i].gpio_num);
252		acpigen_write_string(ctx, info[i].linux_name);
253		acpigen_pop_len(ctx);
254	}
255
256	acpigen_pop_len(ctx);
257	acpigen_pop_len(ctx);
258
259	return 0;
260}
261
262static int coral_write_acpi_tables(const struct udevice *dev,
263				   struct acpi_ctx *ctx)
264{
265	struct acpi_global_nvs *gnvs;
266	struct nhlt *nhlt;
267	const char *oem_id = "coral";
268	const char *oem_table_id = "coral";
269	u32 oem_revision = 3;
270	int ret;
271
272	gnvs = bloblist_find(BLOBLISTT_ACPI_GNVS, sizeof(*gnvs));
273	if (!gnvs)
274		return log_msg_ret("bloblist", -ENOENT);
275
276	nhlt = nhlt_init();
277	if (!nhlt)
278		return -ENOMEM;
279
280	log_debug("Setting up NHLT\n");
281	ret = acpi_setup_nhlt(ctx, nhlt);
282	if (ret)
283		return log_msg_ret("setup", ret);
284
285	/* Update NHLT GNVS Data */
286	gnvs->nhla = (uintptr_t)ctx->current;
287	gnvs->nhll = nhlt_current_size(nhlt);
288
289	ret = nhlt_serialise_oem_overrides(ctx, nhlt, oem_id, oem_table_id,
290					   oem_revision);
291	if (ret)
292		return log_msg_ret("serialise", ret);
293
294	return 0;
295}
296
297struct acpi_ops coral_acpi_ops = {
298	.write_tables	= coral_write_acpi_tables,
299	.inject_dsdt	= chromeos_acpi_gpio_generate,
300};
301
302struct sysinfo_ops coral_sysinfo_ops = {
303	.get_str	= coral_get_str,
304};
305
306#if CONFIG_IS_ENABLED(OF_REAL)
307static const struct udevice_id coral_ids[] = {
308	{ .compatible = "google,coral" },
309	{ }
310};
311#endif
312
313U_BOOT_DRIVER(coral_drv) = {
314	.name		= "coral",
315	.id		= UCLASS_SYSINFO,
316	.of_match	= of_match_ptr(coral_ids),
317	.ops		= &coral_sysinfo_ops,
318	ACPI_OPS_PTR(&coral_acpi_ops)
319};
320