1 /* 2 * System Specific setup for PCEngines ALIX. 3 * At the moment this means setup of GPIO control of LEDs 4 * on Alix.2/3/6 boards. 5 * 6 * 7 * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> 8 * Copyright (C) 2011 Ed Wildgoose <kernel@wildgooses.com> 9 * and Philip Prindeville <philipp@redfish-solutions.com> 10 * 11 * TODO: There are large similarities with leds-net5501.c 12 * by Alessandro Zummo <a.zummo@towertech.it> 13 * In the future leds-net5501.c should be migrated over to platform 14 * 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License version 2 17 * as published by the Free Software Foundation. 18 */ 19 20 #include <linux/kernel.h> 21 #include <linux/init.h> 22 #include <linux/io.h> 23 #include <linux/string.h> 24 #include <linux/moduleparam.h> 25 #include <linux/leds.h> 26 #include <linux/platform_device.h> 27 #include <linux/gpio.h> 28 #include <linux/input.h> 29 #include <linux/gpio_keys.h> 30 #include <linux/dmi.h> 31 32 #include <asm/geode.h> 33 34 #define BIOS_SIGNATURE_TINYBIOS 0xf0000 35 #define BIOS_SIGNATURE_COREBOOT 0x500 36 #define BIOS_REGION_SIZE 0x10000 37 38 /* 39 * This driver is not modular, but to keep back compatibility 40 * with existing use cases, continuing with module_param is 41 * the easiest way forward. 42 */ 43 static bool force = 0; 44 module_param(force, bool, 0444); 45 /* FIXME: Award bios is not automatically detected as Alix platform */ 46 MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); 47 48 static struct gpio_keys_button alix_gpio_buttons[] = { 49 { 50 .code = KEY_RESTART, 51 .gpio = 24, 52 .active_low = 1, 53 .desc = "Reset button", 54 .type = EV_KEY, 55 .wakeup = 0, 56 .debounce_interval = 100, 57 .can_disable = 0, 58 } 59 }; 60 static struct gpio_keys_platform_data alix_buttons_data = { 61 .buttons = alix_gpio_buttons, 62 .nbuttons = ARRAY_SIZE(alix_gpio_buttons), 63 .poll_interval = 20, 64 }; 65 66 static struct platform_device alix_buttons_dev = { 67 .name = "gpio-keys-polled", 68 .id = 1, 69 .dev = { 70 .platform_data = &alix_buttons_data, 71 } 72 }; 73 74 static struct gpio_led alix_leds[] = { 75 { 76 .name = "alix:1", 77 .gpio = 6, 78 .default_trigger = "default-on", 79 .active_low = 1, 80 }, 81 { 82 .name = "alix:2", 83 .gpio = 25, 84 .default_trigger = "default-off", 85 .active_low = 1, 86 }, 87 { 88 .name = "alix:3", 89 .gpio = 27, 90 .default_trigger = "default-off", 91 .active_low = 1, 92 }, 93 }; 94 95 static struct gpio_led_platform_data alix_leds_data = { 96 .num_leds = ARRAY_SIZE(alix_leds), 97 .leds = alix_leds, 98 }; 99 100 static struct platform_device alix_leds_dev = { 101 .name = "leds-gpio", 102 .id = -1, 103 .dev.platform_data = &alix_leds_data, 104 }; 105 106 static struct platform_device *alix_devs[] __initdata = { 107 &alix_buttons_dev, 108 &alix_leds_dev, 109 }; 110 111 static void __init register_alix(void) 112 { 113 /* Setup LED control through leds-gpio driver */ 114 platform_add_devices(alix_devs, ARRAY_SIZE(alix_devs)); 115 } 116 117 static bool __init alix_present(unsigned long bios_phys, 118 const char *alix_sig, 119 size_t alix_sig_len) 120 { 121 const size_t bios_len = BIOS_REGION_SIZE; 122 const char *bios_virt; 123 const char *scan_end; 124 const char *p; 125 char name[64]; 126 127 if (force) { 128 printk(KERN_NOTICE "%s: forced to skip BIOS test, " 129 "assume system is ALIX.2/ALIX.3\n", 130 KBUILD_MODNAME); 131 return true; 132 } 133 134 bios_virt = phys_to_virt(bios_phys); 135 scan_end = bios_virt + bios_len - (alix_sig_len + 2); 136 for (p = bios_virt; p < scan_end; p++) { 137 const char *tail; 138 char *a; 139 140 if (memcmp(p, alix_sig, alix_sig_len) != 0) 141 continue; 142 143 memcpy(name, p, sizeof(name)); 144 145 /* remove the first \0 character from string */ 146 a = strchr(name, '\0'); 147 if (a) 148 *a = ' '; 149 150 /* cut the string at a newline */ 151 a = strchr(name, '\r'); 152 if (a) 153 *a = '\0'; 154 155 tail = p + alix_sig_len; 156 if ((tail[0] == '2' || tail[0] == '3' || tail[0] == '6')) { 157 printk(KERN_INFO 158 "%s: system is recognized as \"%s\"\n", 159 KBUILD_MODNAME, name); 160 return true; 161 } 162 } 163 164 return false; 165 } 166 167 static bool __init alix_present_dmi(void) 168 { 169 const char *vendor, *product; 170 171 vendor = dmi_get_system_info(DMI_SYS_VENDOR); 172 if (!vendor || strcmp(vendor, "PC Engines")) 173 return false; 174 175 product = dmi_get_system_info(DMI_PRODUCT_NAME); 176 if (!product || (strcmp(product, "ALIX.2D") && strcmp(product, "ALIX.6"))) 177 return false; 178 179 printk(KERN_INFO "%s: system is recognized as \"%s %s\"\n", 180 KBUILD_MODNAME, vendor, product); 181 182 return true; 183 } 184 185 static int __init alix_init(void) 186 { 187 const char tinybios_sig[] = "PC Engines ALIX."; 188 const char coreboot_sig[] = "PC Engines\0ALIX."; 189 190 if (!is_geode()) 191 return 0; 192 193 if (alix_present(BIOS_SIGNATURE_TINYBIOS, tinybios_sig, sizeof(tinybios_sig) - 1) || 194 alix_present(BIOS_SIGNATURE_COREBOOT, coreboot_sig, sizeof(coreboot_sig) - 1) || 195 alix_present_dmi()) 196 register_alix(); 197 198 return 0; 199 } 200 device_initcall(alix_init); 201