linux/arch/x86/platform/geode/alix.c
<<
>>
Prefs
   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 */
  43static bool force = 0;
  44module_param(force, bool, 0444);
  45/* FIXME: Award bios is not automatically detected as Alix platform */
  46MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform");
  47
  48static 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};
  60static 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
  66static 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
  74static 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
  95static struct gpio_led_platform_data alix_leds_data = {
  96        .num_leds = ARRAY_SIZE(alix_leds),
  97        .leds = alix_leds,
  98};
  99
 100static struct platform_device alix_leds_dev = {
 101        .name = "leds-gpio",
 102        .id = -1,
 103        .dev.platform_data = &alix_leds_data,
 104};
 105
 106static struct platform_device *alix_devs[] __initdata = {
 107        &alix_buttons_dev,
 108        &alix_leds_dev,
 109};
 110
 111static 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
 117static 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
 167static 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
 185static 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}
 200device_initcall(alix_init);
 201