xref: /linux/arch/x86/platform/geode/alix.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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>
21373913b5SPhilip Prindeville #include <linux/dmi.h>
22d4f3e350SEd Wildgoose 
23d4f3e350SEd Wildgoose #include <asm/geode.h>
24d4f3e350SEd Wildgoose 
25*298c9babSDmitry Torokhov #include "geode-common.h"
26*298c9babSDmitry Torokhov 
27373913b5SPhilip Prindeville #define BIOS_SIGNATURE_TINYBIOS		0xf0000
28373913b5SPhilip Prindeville #define BIOS_SIGNATURE_COREBOOT		0x500
29373913b5SPhilip Prindeville #define BIOS_REGION_SIZE		0x10000
30373913b5SPhilip Prindeville 
3152d856e8SPaul Gortmaker /*
3252d856e8SPaul Gortmaker  * This driver is not modular, but to keep back compatibility
3352d856e8SPaul Gortmaker  * with existing use cases, continuing with module_param is
3452d856e8SPaul Gortmaker  * the easiest way forward.
3552d856e8SPaul Gortmaker  */
36476bc001SRusty Russell static bool force = 0;
37d4f3e350SEd Wildgoose module_param(force, bool, 0444);
38d4f3e350SEd Wildgoose /* FIXME: Award bios is not automatically detected as Alix platform */
39d4f3e350SEd Wildgoose MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform");
40d4f3e350SEd Wildgoose 
41*298c9babSDmitry Torokhov static const struct geode_led alix_leds[] __initconst = {
42*298c9babSDmitry Torokhov 	{ 6, true },
43*298c9babSDmitry Torokhov 	{ 25, false },
44*298c9babSDmitry Torokhov 	{ 27, false },
45373913b5SPhilip Prindeville };
46373913b5SPhilip Prindeville 
register_alix(void)47d4f3e350SEd Wildgoose static void __init register_alix(void)
48d4f3e350SEd Wildgoose {
49*298c9babSDmitry Torokhov 	geode_create_restart_key(24);
50*298c9babSDmitry Torokhov 	geode_create_leds("alix", alix_leds, ARRAY_SIZE(alix_leds));
51d4f3e350SEd Wildgoose }
52d4f3e350SEd Wildgoose 
alix_present(unsigned long bios_phys,const char * alix_sig,size_t alix_sig_len)53373913b5SPhilip Prindeville static bool __init alix_present(unsigned long bios_phys,
54d4f3e350SEd Wildgoose 				const char *alix_sig,
55d4f3e350SEd Wildgoose 				size_t alix_sig_len)
56d4f3e350SEd Wildgoose {
57373913b5SPhilip Prindeville 	const size_t bios_len = BIOS_REGION_SIZE;
58d4f3e350SEd Wildgoose 	const char *bios_virt;
59d4f3e350SEd Wildgoose 	const char *scan_end;
60d4f3e350SEd Wildgoose 	const char *p;
61d4f3e350SEd Wildgoose 	char name[64];
62d4f3e350SEd Wildgoose 
63d4f3e350SEd Wildgoose 	if (force) {
64d4f3e350SEd Wildgoose 		printk(KERN_NOTICE "%s: forced to skip BIOS test, "
65d4f3e350SEd Wildgoose 		       "assume system is ALIX.2/ALIX.3\n",
66d4f3e350SEd Wildgoose 		       KBUILD_MODNAME);
67373913b5SPhilip Prindeville 		return true;
68d4f3e350SEd Wildgoose 	}
69d4f3e350SEd Wildgoose 
70d4f3e350SEd Wildgoose 	bios_virt = phys_to_virt(bios_phys);
71d4f3e350SEd Wildgoose 	scan_end = bios_virt + bios_len - (alix_sig_len + 2);
72d4f3e350SEd Wildgoose 	for (p = bios_virt; p < scan_end; p++) {
73d4f3e350SEd Wildgoose 		const char *tail;
74d4f3e350SEd Wildgoose 		char *a;
75d4f3e350SEd Wildgoose 
76d4f3e350SEd Wildgoose 		if (memcmp(p, alix_sig, alix_sig_len) != 0)
77d4f3e350SEd Wildgoose 			continue;
78d4f3e350SEd Wildgoose 
79d4f3e350SEd Wildgoose 		memcpy(name, p, sizeof(name));
80d4f3e350SEd Wildgoose 
81d4f3e350SEd Wildgoose 		/* remove the first \0 character from string */
82d4f3e350SEd Wildgoose 		a = strchr(name, '\0');
83d4f3e350SEd Wildgoose 		if (a)
84d4f3e350SEd Wildgoose 			*a = ' ';
85d4f3e350SEd Wildgoose 
86d4f3e350SEd Wildgoose 		/* cut the string at a newline */
87d4f3e350SEd Wildgoose 		a = strchr(name, '\r');
88d4f3e350SEd Wildgoose 		if (a)
89d4f3e350SEd Wildgoose 			*a = '\0';
90d4f3e350SEd Wildgoose 
91d4f3e350SEd Wildgoose 		tail = p + alix_sig_len;
92373913b5SPhilip Prindeville 		if ((tail[0] == '2' || tail[0] == '3' || tail[0] == '6')) {
93d4f3e350SEd Wildgoose 			printk(KERN_INFO
94d4f3e350SEd Wildgoose 			       "%s: system is recognized as \"%s\"\n",
95d4f3e350SEd Wildgoose 			       KBUILD_MODNAME, name);
96373913b5SPhilip Prindeville 			return true;
97d4f3e350SEd Wildgoose 		}
98d4f3e350SEd Wildgoose 	}
99d4f3e350SEd Wildgoose 
100373913b5SPhilip Prindeville 	return false;
101373913b5SPhilip Prindeville }
102373913b5SPhilip Prindeville 
alix_present_dmi(void)103373913b5SPhilip Prindeville static bool __init alix_present_dmi(void)
104373913b5SPhilip Prindeville {
105373913b5SPhilip Prindeville 	const char *vendor, *product;
106373913b5SPhilip Prindeville 
107373913b5SPhilip Prindeville 	vendor = dmi_get_system_info(DMI_SYS_VENDOR);
108373913b5SPhilip Prindeville 	if (!vendor || strcmp(vendor, "PC Engines"))
109373913b5SPhilip Prindeville 		return false;
110373913b5SPhilip Prindeville 
111373913b5SPhilip Prindeville 	product = dmi_get_system_info(DMI_PRODUCT_NAME);
112373913b5SPhilip Prindeville 	if (!product || (strcmp(product, "ALIX.2D") && strcmp(product, "ALIX.6")))
113373913b5SPhilip Prindeville 		return false;
114373913b5SPhilip Prindeville 
115373913b5SPhilip Prindeville 	printk(KERN_INFO "%s: system is recognized as \"%s %s\"\n",
116373913b5SPhilip Prindeville 	       KBUILD_MODNAME, vendor, product);
117373913b5SPhilip Prindeville 
118373913b5SPhilip Prindeville 	return true;
119d4f3e350SEd Wildgoose }
120d4f3e350SEd Wildgoose 
alix_init(void)121d4f3e350SEd Wildgoose static int __init alix_init(void)
122d4f3e350SEd Wildgoose {
123d4f3e350SEd Wildgoose 	const char tinybios_sig[] = "PC Engines ALIX.";
124d4f3e350SEd Wildgoose 	const char coreboot_sig[] = "PC Engines\0ALIX.";
125d4f3e350SEd Wildgoose 
126d4f3e350SEd Wildgoose 	if (!is_geode())
127d4f3e350SEd Wildgoose 		return 0;
128d4f3e350SEd Wildgoose 
129373913b5SPhilip Prindeville 	if (alix_present(BIOS_SIGNATURE_TINYBIOS, tinybios_sig, sizeof(tinybios_sig) - 1) ||
130373913b5SPhilip Prindeville 	    alix_present(BIOS_SIGNATURE_COREBOOT, coreboot_sig, sizeof(coreboot_sig) - 1) ||
131373913b5SPhilip Prindeville 	    alix_present_dmi())
132d4f3e350SEd Wildgoose 		register_alix();
133d4f3e350SEd Wildgoose 
134d4f3e350SEd Wildgoose 	return 0;
135d4f3e350SEd Wildgoose }
13652d856e8SPaul Gortmaker device_initcall(alix_init);
137