Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * System Specific setup for PCEngines ALIX.
0004  * At the moment this means setup of GPIO control of LEDs
0005  * on Alix.2/3/6 boards.
0006  *
0007  * Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
0008  * Copyright (C) 2011 Ed Wildgoose <kernel@wildgooses.com>
0009  *                and Philip Prindeville <philipp@redfish-solutions.com>
0010  *
0011  * TODO: There are large similarities with leds-net5501.c
0012  * by Alessandro Zummo <a.zummo@towertech.it>
0013  * In the future leds-net5501.c should be migrated over to platform
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  * This driver is not modular, but to keep back compatibility
0036  * with existing use cases, continuing with module_param is
0037  * the easiest way forward.
0038  */
0039 static bool force = 0;
0040 module_param(force, bool, 0444);
0041 /* FIXME: Award bios is not automatically detected as Alix platform */
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         /* The Geode GPIOs should be on the CS5535 companion chip */
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     /* Setup LED control through leds-gpio driver */
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         /* remove the first \0 character from string */
0148         a = strchr(name, '\0');
0149         if (a)
0150             *a = ' ';
0151 
0152         /* cut the string at a newline */
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);