xref: /openbmc/linux/arch/x86/platform/geode/alix.c (revision 60430301)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d4f3e350SEd Wildgoose /*
3d4f3e350SEd Wildgoose  * System Specific setup for PCEngines ALIX.
4d4f3e350SEd Wildgoose  * At the moment this means setup of GPIO control of LEDs
5d4f3e350SEd Wildgoose  * on Alix.2/3/6 boards.
6d4f3e350SEd Wildgoose  *
7d4f3e350SEd Wildgoose  * Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
8d4f3e350SEd Wildgoose  * Copyright (C) 2011 Ed Wildgoose <kernel@wildgooses.com>
9373913b5SPhilip Prindeville  *                and Philip Prindeville <philipp@redfish-solutions.com>
10d4f3e350SEd Wildgoose  *
11d4f3e350SEd Wildgoose  * TODO: There are large similarities with leds-net5501.c
12d4f3e350SEd Wildgoose  * by Alessandro Zummo <a.zummo@towertech.it>
13d4f3e350SEd Wildgoose  * In the future leds-net5501.c should be migrated over to platform
14d4f3e350SEd Wildgoose  */
15d4f3e350SEd Wildgoose 
16d4f3e350SEd Wildgoose #include <linux/kernel.h>
17d4f3e350SEd Wildgoose #include <linux/init.h>
18d4f3e350SEd Wildgoose #include <linux/io.h>
19d4f3e350SEd Wildgoose #include <linux/string.h>
2052d856e8SPaul Gortmaker #include <linux/moduleparam.h>
21d4f3e350SEd Wildgoose #include <linux/leds.h>
22d4f3e350SEd Wildgoose #include <linux/platform_device.h>
23373913b5SPhilip Prindeville #include <linux/input.h>
24373913b5SPhilip Prindeville #include <linux/gpio_keys.h>
25*60430301SLinus Walleij #include <linux/gpio/machine.h>
26373913b5SPhilip Prindeville #include <linux/dmi.h>
27d4f3e350SEd Wildgoose 
28d4f3e350SEd Wildgoose #include <asm/geode.h>
29d4f3e350SEd Wildgoose 
30373913b5SPhilip Prindeville #define BIOS_SIGNATURE_TINYBIOS		0xf0000
31373913b5SPhilip Prindeville #define BIOS_SIGNATURE_COREBOOT		0x500
32373913b5SPhilip Prindeville #define BIOS_REGION_SIZE		0x10000
33373913b5SPhilip Prindeville 
3452d856e8SPaul Gortmaker /*
3552d856e8SPaul Gortmaker  * This driver is not modular, but to keep back compatibility
3652d856e8SPaul Gortmaker  * with existing use cases, continuing with module_param is
3752d856e8SPaul Gortmaker  * the easiest way forward.
3852d856e8SPaul Gortmaker  */
39476bc001SRusty Russell static bool force = 0;
40d4f3e350SEd Wildgoose module_param(force, bool, 0444);
41d4f3e350SEd Wildgoose /* FIXME: Award bios is not automatically detected as Alix platform */
42d4f3e350SEd Wildgoose MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform");
43d4f3e350SEd Wildgoose 
44373913b5SPhilip Prindeville static struct gpio_keys_button alix_gpio_buttons[] = {
45373913b5SPhilip Prindeville 	{
46373913b5SPhilip Prindeville 		.code			= KEY_RESTART,
47373913b5SPhilip Prindeville 		.gpio			= 24,
48373913b5SPhilip Prindeville 		.active_low		= 1,
49373913b5SPhilip Prindeville 		.desc			= "Reset button",
50373913b5SPhilip Prindeville 		.type			= EV_KEY,
51373913b5SPhilip Prindeville 		.wakeup			= 0,
52373913b5SPhilip Prindeville 		.debounce_interval	= 100,
53373913b5SPhilip Prindeville 		.can_disable		= 0,
54373913b5SPhilip Prindeville 	}
55373913b5SPhilip Prindeville };
56373913b5SPhilip Prindeville static struct gpio_keys_platform_data alix_buttons_data = {
57373913b5SPhilip Prindeville 	.buttons			= alix_gpio_buttons,
58373913b5SPhilip Prindeville 	.nbuttons			= ARRAY_SIZE(alix_gpio_buttons),
59373913b5SPhilip Prindeville 	.poll_interval			= 20,
60373913b5SPhilip Prindeville };
61373913b5SPhilip Prindeville 
62373913b5SPhilip Prindeville static struct platform_device alix_buttons_dev = {
63373913b5SPhilip Prindeville 	.name				= "gpio-keys-polled",
64373913b5SPhilip Prindeville 	.id				= 1,
65373913b5SPhilip Prindeville 	.dev = {
66373913b5SPhilip Prindeville 		.platform_data		= &alix_buttons_data,
67373913b5SPhilip Prindeville 	}
68373913b5SPhilip Prindeville };
69373913b5SPhilip Prindeville 
70d4f3e350SEd Wildgoose static struct gpio_led alix_leds[] = {
71d4f3e350SEd Wildgoose 	{
72d4f3e350SEd Wildgoose 		.name = "alix:1",
73d4f3e350SEd Wildgoose 		.default_trigger = "default-on",
74d4f3e350SEd Wildgoose 	},
75d4f3e350SEd Wildgoose 	{
76d4f3e350SEd Wildgoose 		.name = "alix:2",
77d4f3e350SEd Wildgoose 		.default_trigger = "default-off",
78d4f3e350SEd Wildgoose 	},
79d4f3e350SEd Wildgoose 	{
80d4f3e350SEd Wildgoose 		.name = "alix:3",
81d4f3e350SEd Wildgoose 		.default_trigger = "default-off",
82d4f3e350SEd Wildgoose 	},
83d4f3e350SEd Wildgoose };
84d4f3e350SEd Wildgoose 
85d4f3e350SEd Wildgoose static struct gpio_led_platform_data alix_leds_data = {
86d4f3e350SEd Wildgoose 	.num_leds = ARRAY_SIZE(alix_leds),
87d4f3e350SEd Wildgoose 	.leds = alix_leds,
88d4f3e350SEd Wildgoose };
89d4f3e350SEd Wildgoose 
90*60430301SLinus Walleij static struct gpiod_lookup_table alix_leds_gpio_table = {
91*60430301SLinus Walleij 	.dev_id = "leds-gpio",
92*60430301SLinus Walleij 	.table = {
93*60430301SLinus Walleij 		/* The Geode GPIOs should be on the CS5535 companion chip */
94*60430301SLinus Walleij 		GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_LOW),
95*60430301SLinus Walleij 		GPIO_LOOKUP_IDX("cs5535-gpio", 25, NULL, 1, GPIO_ACTIVE_LOW),
96*60430301SLinus Walleij 		GPIO_LOOKUP_IDX("cs5535-gpio", 27, NULL, 2, GPIO_ACTIVE_LOW),
97*60430301SLinus Walleij 		{ }
98*60430301SLinus Walleij 	},
99*60430301SLinus Walleij };
100*60430301SLinus Walleij 
101d4f3e350SEd Wildgoose static struct platform_device alix_leds_dev = {
102d4f3e350SEd Wildgoose 	.name = "leds-gpio",
103d4f3e350SEd Wildgoose 	.id = -1,
104d4f3e350SEd Wildgoose 	.dev.platform_data = &alix_leds_data,
105d4f3e350SEd Wildgoose };
106d4f3e350SEd Wildgoose 
107a6d30e0fSBartlomiej Zolnierkiewicz static struct platform_device *alix_devs[] __initdata = {
108373913b5SPhilip Prindeville 	&alix_buttons_dev,
109373913b5SPhilip Prindeville 	&alix_leds_dev,
110373913b5SPhilip Prindeville };
111373913b5SPhilip Prindeville 
register_alix(void)112d4f3e350SEd Wildgoose static void __init register_alix(void)
113d4f3e350SEd Wildgoose {
114d4f3e350SEd Wildgoose 	/* Setup LED control through leds-gpio driver */
115*60430301SLinus Walleij 	gpiod_add_lookup_table(&alix_leds_gpio_table);
116373913b5SPhilip Prindeville 	platform_add_devices(alix_devs, ARRAY_SIZE(alix_devs));
117d4f3e350SEd Wildgoose }
118d4f3e350SEd Wildgoose 
alix_present(unsigned long bios_phys,const char * alix_sig,size_t alix_sig_len)119373913b5SPhilip Prindeville static bool __init alix_present(unsigned long bios_phys,
120d4f3e350SEd Wildgoose 				const char *alix_sig,
121d4f3e350SEd Wildgoose 				size_t alix_sig_len)
122d4f3e350SEd Wildgoose {
123373913b5SPhilip Prindeville 	const size_t bios_len = BIOS_REGION_SIZE;
124d4f3e350SEd Wildgoose 	const char *bios_virt;
125d4f3e350SEd Wildgoose 	const char *scan_end;
126d4f3e350SEd Wildgoose 	const char *p;
127d4f3e350SEd Wildgoose 	char name[64];
128d4f3e350SEd Wildgoose 
129d4f3e350SEd Wildgoose 	if (force) {
130d4f3e350SEd Wildgoose 		printk(KERN_NOTICE "%s: forced to skip BIOS test, "
131d4f3e350SEd Wildgoose 		       "assume system is ALIX.2/ALIX.3\n",
132d4f3e350SEd Wildgoose 		       KBUILD_MODNAME);
133373913b5SPhilip Prindeville 		return true;
134d4f3e350SEd Wildgoose 	}
135d4f3e350SEd Wildgoose 
136d4f3e350SEd Wildgoose 	bios_virt = phys_to_virt(bios_phys);
137d4f3e350SEd Wildgoose 	scan_end = bios_virt + bios_len - (alix_sig_len + 2);
138d4f3e350SEd Wildgoose 	for (p = bios_virt; p < scan_end; p++) {
139d4f3e350SEd Wildgoose 		const char *tail;
140d4f3e350SEd Wildgoose 		char *a;
141d4f3e350SEd Wildgoose 
142d4f3e350SEd Wildgoose 		if (memcmp(p, alix_sig, alix_sig_len) != 0)
143d4f3e350SEd Wildgoose 			continue;
144d4f3e350SEd Wildgoose 
145d4f3e350SEd Wildgoose 		memcpy(name, p, sizeof(name));
146d4f3e350SEd Wildgoose 
147d4f3e350SEd Wildgoose 		/* remove the first \0 character from string */
148d4f3e350SEd Wildgoose 		a = strchr(name, '\0');
149d4f3e350SEd Wildgoose 		if (a)
150d4f3e350SEd Wildgoose 			*a = ' ';
151d4f3e350SEd Wildgoose 
152d4f3e350SEd Wildgoose 		/* cut the string at a newline */
153d4f3e350SEd Wildgoose 		a = strchr(name, '\r');
154d4f3e350SEd Wildgoose 		if (a)
155d4f3e350SEd Wildgoose 			*a = '\0';
156d4f3e350SEd Wildgoose 
157d4f3e350SEd Wildgoose 		tail = p + alix_sig_len;
158373913b5SPhilip Prindeville 		if ((tail[0] == '2' || tail[0] == '3' || tail[0] == '6')) {
159d4f3e350SEd Wildgoose 			printk(KERN_INFO
160d4f3e350SEd Wildgoose 			       "%s: system is recognized as \"%s\"\n",
161d4f3e350SEd Wildgoose 			       KBUILD_MODNAME, name);
162373913b5SPhilip Prindeville 			return true;
163d4f3e350SEd Wildgoose 		}
164d4f3e350SEd Wildgoose 	}
165d4f3e350SEd Wildgoose 
166373913b5SPhilip Prindeville 	return false;
167373913b5SPhilip Prindeville }
168373913b5SPhilip Prindeville 
alix_present_dmi(void)169373913b5SPhilip Prindeville static bool __init alix_present_dmi(void)
170373913b5SPhilip Prindeville {
171373913b5SPhilip Prindeville 	const char *vendor, *product;
172373913b5SPhilip Prindeville 
173373913b5SPhilip Prindeville 	vendor = dmi_get_system_info(DMI_SYS_VENDOR);
174373913b5SPhilip Prindeville 	if (!vendor || strcmp(vendor, "PC Engines"))
175373913b5SPhilip Prindeville 		return false;
176373913b5SPhilip Prindeville 
177373913b5SPhilip Prindeville 	product = dmi_get_system_info(DMI_PRODUCT_NAME);
178373913b5SPhilip Prindeville 	if (!product || (strcmp(product, "ALIX.2D") && strcmp(product, "ALIX.6")))
179373913b5SPhilip Prindeville 		return false;
180373913b5SPhilip Prindeville 
181373913b5SPhilip Prindeville 	printk(KERN_INFO "%s: system is recognized as \"%s %s\"\n",
182373913b5SPhilip Prindeville 	       KBUILD_MODNAME, vendor, product);
183373913b5SPhilip Prindeville 
184373913b5SPhilip Prindeville 	return true;
185d4f3e350SEd Wildgoose }
186d4f3e350SEd Wildgoose 
alix_init(void)187d4f3e350SEd Wildgoose static int __init alix_init(void)
188d4f3e350SEd Wildgoose {
189d4f3e350SEd Wildgoose 	const char tinybios_sig[] = "PC Engines ALIX.";
190d4f3e350SEd Wildgoose 	const char coreboot_sig[] = "PC Engines\0ALIX.";
191d4f3e350SEd Wildgoose 
192d4f3e350SEd Wildgoose 	if (!is_geode())
193d4f3e350SEd Wildgoose 		return 0;
194d4f3e350SEd Wildgoose 
195373913b5SPhilip Prindeville 	if (alix_present(BIOS_SIGNATURE_TINYBIOS, tinybios_sig, sizeof(tinybios_sig) - 1) ||
196373913b5SPhilip Prindeville 	    alix_present(BIOS_SIGNATURE_COREBOOT, coreboot_sig, sizeof(coreboot_sig) - 1) ||
197373913b5SPhilip Prindeville 	    alix_present_dmi())
198d4f3e350SEd Wildgoose 		register_alix();
199d4f3e350SEd Wildgoose 
200d4f3e350SEd Wildgoose 	return 0;
201d4f3e350SEd Wildgoose }
20252d856e8SPaul Gortmaker device_initcall(alix_init);
203