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