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 * 10 * TODO: There are large similarities with leds-net5501.c 11 * by Alessandro Zummo <a.zummo@towertech.it> 12 * In the future leds-net5501.c should be migrated over to platform 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License version 2 16 * as published by the Free Software Foundation. 17 */ 18 19 #include <linux/kernel.h> 20 #include <linux/init.h> 21 #include <linux/io.h> 22 #include <linux/string.h> 23 #include <linux/module.h> 24 #include <linux/leds.h> 25 #include <linux/platform_device.h> 26 #include <linux/gpio.h> 27 28 #include <asm/geode.h> 29 30 static int force = 0; 31 module_param(force, bool, 0444); 32 /* FIXME: Award bios is not automatically detected as Alix platform */ 33 MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); 34 35 static struct gpio_led alix_leds[] = { 36 { 37 .name = "alix:1", 38 .gpio = 6, 39 .default_trigger = "default-on", 40 .active_low = 1, 41 }, 42 { 43 .name = "alix:2", 44 .gpio = 25, 45 .default_trigger = "default-off", 46 .active_low = 1, 47 }, 48 { 49 .name = "alix:3", 50 .gpio = 27, 51 .default_trigger = "default-off", 52 .active_low = 1, 53 }, 54 }; 55 56 static struct gpio_led_platform_data alix_leds_data = { 57 .num_leds = ARRAY_SIZE(alix_leds), 58 .leds = alix_leds, 59 }; 60 61 static struct platform_device alix_leds_dev = { 62 .name = "leds-gpio", 63 .id = -1, 64 .dev.platform_data = &alix_leds_data, 65 }; 66 67 static void __init register_alix(void) 68 { 69 /* Setup LED control through leds-gpio driver */ 70 platform_device_register(&alix_leds_dev); 71 } 72 73 static int __init alix_present(unsigned long bios_phys, 74 const char *alix_sig, 75 size_t alix_sig_len) 76 { 77 const size_t bios_len = 0x00010000; 78 const char *bios_virt; 79 const char *scan_end; 80 const char *p; 81 char name[64]; 82 83 if (force) { 84 printk(KERN_NOTICE "%s: forced to skip BIOS test, " 85 "assume system is ALIX.2/ALIX.3\n", 86 KBUILD_MODNAME); 87 return 1; 88 } 89 90 bios_virt = phys_to_virt(bios_phys); 91 scan_end = bios_virt + bios_len - (alix_sig_len + 2); 92 for (p = bios_virt; p < scan_end; p++) { 93 const char *tail; 94 char *a; 95 96 if (memcmp(p, alix_sig, alix_sig_len) != 0) 97 continue; 98 99 memcpy(name, p, sizeof(name)); 100 101 /* remove the first \0 character from string */ 102 a = strchr(name, '\0'); 103 if (a) 104 *a = ' '; 105 106 /* cut the string at a newline */ 107 a = strchr(name, '\r'); 108 if (a) 109 *a = '\0'; 110 111 tail = p + alix_sig_len; 112 if ((tail[0] == '2' || tail[0] == '3')) { 113 printk(KERN_INFO 114 "%s: system is recognized as \"%s\"\n", 115 KBUILD_MODNAME, name); 116 return 1; 117 } 118 } 119 120 return 0; 121 } 122 123 static int __init alix_init(void) 124 { 125 const char tinybios_sig[] = "PC Engines ALIX."; 126 const char coreboot_sig[] = "PC Engines\0ALIX."; 127 128 if (!is_geode()) 129 return 0; 130 131 if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || 132 alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) 133 register_alix(); 134 135 return 0; 136 } 137 138 module_init(alix_init); 139 140 MODULE_AUTHOR("Ed Wildgoose <kernel@wildgooses.com>"); 141 MODULE_DESCRIPTION("PCEngines ALIX System Setup"); 142 MODULE_LICENSE("GPL"); 143