0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include <linux/kernel.h>
0017 #include <linux/init.h>
0018 #include <linux/io.h>
0019 #include <linux/string.h>
0020 #include <linux/moduleparam.h>
0021 #include <linux/leds.h>
0022 #include <linux/platform_device.h>
0023 #include <linux/input.h>
0024 #include <linux/gpio_keys.h>
0025 #include <linux/gpio/machine.h>
0026 #include <linux/dmi.h>
0027
0028 #include <asm/geode.h>
0029
0030 #define BIOS_SIGNATURE_TINYBIOS 0xf0000
0031 #define BIOS_SIGNATURE_COREBOOT 0x500
0032 #define BIOS_REGION_SIZE 0x10000
0033
0034
0035
0036
0037
0038
0039 static bool force = 0;
0040 module_param(force, bool, 0444);
0041
0042 MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform");
0043
0044 static struct gpio_keys_button alix_gpio_buttons[] = {
0045 {
0046 .code = KEY_RESTART,
0047 .gpio = 24,
0048 .active_low = 1,
0049 .desc = "Reset button",
0050 .type = EV_KEY,
0051 .wakeup = 0,
0052 .debounce_interval = 100,
0053 .can_disable = 0,
0054 }
0055 };
0056 static struct gpio_keys_platform_data alix_buttons_data = {
0057 .buttons = alix_gpio_buttons,
0058 .nbuttons = ARRAY_SIZE(alix_gpio_buttons),
0059 .poll_interval = 20,
0060 };
0061
0062 static struct platform_device alix_buttons_dev = {
0063 .name = "gpio-keys-polled",
0064 .id = 1,
0065 .dev = {
0066 .platform_data = &alix_buttons_data,
0067 }
0068 };
0069
0070 static struct gpio_led alix_leds[] = {
0071 {
0072 .name = "alix:1",
0073 .default_trigger = "default-on",
0074 },
0075 {
0076 .name = "alix:2",
0077 .default_trigger = "default-off",
0078 },
0079 {
0080 .name = "alix:3",
0081 .default_trigger = "default-off",
0082 },
0083 };
0084
0085 static struct gpio_led_platform_data alix_leds_data = {
0086 .num_leds = ARRAY_SIZE(alix_leds),
0087 .leds = alix_leds,
0088 };
0089
0090 static struct gpiod_lookup_table alix_leds_gpio_table = {
0091 .dev_id = "leds-gpio",
0092 .table = {
0093
0094 GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_LOW),
0095 GPIO_LOOKUP_IDX("cs5535-gpio", 25, NULL, 1, GPIO_ACTIVE_LOW),
0096 GPIO_LOOKUP_IDX("cs5535-gpio", 27, NULL, 2, GPIO_ACTIVE_LOW),
0097 { }
0098 },
0099 };
0100
0101 static struct platform_device alix_leds_dev = {
0102 .name = "leds-gpio",
0103 .id = -1,
0104 .dev.platform_data = &alix_leds_data,
0105 };
0106
0107 static struct platform_device *alix_devs[] __initdata = {
0108 &alix_buttons_dev,
0109 &alix_leds_dev,
0110 };
0111
0112 static void __init register_alix(void)
0113 {
0114
0115 gpiod_add_lookup_table(&alix_leds_gpio_table);
0116 platform_add_devices(alix_devs, ARRAY_SIZE(alix_devs));
0117 }
0118
0119 static bool __init alix_present(unsigned long bios_phys,
0120 const char *alix_sig,
0121 size_t alix_sig_len)
0122 {
0123 const size_t bios_len = BIOS_REGION_SIZE;
0124 const char *bios_virt;
0125 const char *scan_end;
0126 const char *p;
0127 char name[64];
0128
0129 if (force) {
0130 printk(KERN_NOTICE "%s: forced to skip BIOS test, "
0131 "assume system is ALIX.2/ALIX.3\n",
0132 KBUILD_MODNAME);
0133 return true;
0134 }
0135
0136 bios_virt = phys_to_virt(bios_phys);
0137 scan_end = bios_virt + bios_len - (alix_sig_len + 2);
0138 for (p = bios_virt; p < scan_end; p++) {
0139 const char *tail;
0140 char *a;
0141
0142 if (memcmp(p, alix_sig, alix_sig_len) != 0)
0143 continue;
0144
0145 memcpy(name, p, sizeof(name));
0146
0147
0148 a = strchr(name, '\0');
0149 if (a)
0150 *a = ' ';
0151
0152
0153 a = strchr(name, '\r');
0154 if (a)
0155 *a = '\0';
0156
0157 tail = p + alix_sig_len;
0158 if ((tail[0] == '2' || tail[0] == '3' || tail[0] == '6')) {
0159 printk(KERN_INFO
0160 "%s: system is recognized as \"%s\"\n",
0161 KBUILD_MODNAME, name);
0162 return true;
0163 }
0164 }
0165
0166 return false;
0167 }
0168
0169 static bool __init alix_present_dmi(void)
0170 {
0171 const char *vendor, *product;
0172
0173 vendor = dmi_get_system_info(DMI_SYS_VENDOR);
0174 if (!vendor || strcmp(vendor, "PC Engines"))
0175 return false;
0176
0177 product = dmi_get_system_info(DMI_PRODUCT_NAME);
0178 if (!product || (strcmp(product, "ALIX.2D") && strcmp(product, "ALIX.6")))
0179 return false;
0180
0181 printk(KERN_INFO "%s: system is recognized as \"%s %s\"\n",
0182 KBUILD_MODNAME, vendor, product);
0183
0184 return true;
0185 }
0186
0187 static int __init alix_init(void)
0188 {
0189 const char tinybios_sig[] = "PC Engines ALIX.";
0190 const char coreboot_sig[] = "PC Engines\0ALIX.";
0191
0192 if (!is_geode())
0193 return 0;
0194
0195 if (alix_present(BIOS_SIGNATURE_TINYBIOS, tinybios_sig, sizeof(tinybios_sig) - 1) ||
0196 alix_present(BIOS_SIGNATURE_COREBOOT, coreboot_sig, sizeof(coreboot_sig) - 1) ||
0197 alix_present_dmi())
0198 register_alix();
0199
0200 return 0;
0201 }
0202 device_initcall(alix_init);