leds: leds-alix2c - take port address from MSR
authorDaniel Mack <daniel@caiaq.de>
Mon, 5 Oct 2009 12:22:27 +0000 (20:22 +0800)
committerRichard Purdie <rpurdie@linux.intel.com>
Wed, 16 Dec 2009 11:30:09 +0000 (11:30 +0000)
This makes the LEDs driver for ALIX2.C boards work with Coreboot by
looking up the port address in the MSR rather than hard-coding it.

The BIOS scan also needed some tweaks as the string in Coreboot differs
from the one in the legacy BIOS.

Successfully tested with both the legacy tinyBIOS as well as Coreboot
v3.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
drivers/leds/leds-alix2.c

index 731d4ee..f59ffad 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
+#include <linux/pci.h>
 
 static int force = 0;
 module_param(force, bool, 0444);
 MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
 
+#define MSR_LBAR_GPIO          0x5140000C
+#define CS5535_GPIO_SIZE       256
+
+static u32 gpio_base;
+
+static struct pci_device_id divil_pci[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_NS,  PCI_DEVICE_ID_NS_CS5535_ISA) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+       { } /* NULL entry */
+};
+MODULE_DEVICE_TABLE(pci, divil_pci);
+
 struct alix_led {
        struct led_classdev cdev;
        unsigned short port;
@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev,
                container_of(led_cdev, struct alix_led, cdev);
 
        if (brightness)
-               outl(led_dev->on_value, led_dev->port);
+               outl(led_dev->on_value, gpio_base + led_dev->port);
        else
-               outl(led_dev->off_value, led_dev->port);
+               outl(led_dev->off_value, gpio_base + led_dev->port);
 }
 
 static struct alix_led alix_leds[] = {
@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = {
                        .name = "alix:1",
                        .brightness_set = alix_led_set,
                },
-               .port = 0x6100,
+               .port = 0x00,
                .on_value = 1 << 22,
                .off_value = 1 << 6,
        },
@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = {
                        .name = "alix:2",
                        .brightness_set = alix_led_set,
                },
-               .port = 0x6180,
+               .port = 0x80,
                .on_value = 1 << 25,
                .off_value = 1 << 9,
        },
@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = {
                        .name = "alix:3",
                        .brightness_set = alix_led_set,
                },
-               .port = 0x6180,
+               .port = 0x80,
                .on_value = 1 << 27,
                .off_value = 1 << 11,
        },
@@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = {
        },
 };
 
-static int __init alix_present(void)
+static int __init alix_present(unsigned long bios_phys,
+                               const char *alix_sig,
+                               size_t alix_sig_len)
 {
-       const unsigned long bios_phys = 0x000f0000;
        const size_t bios_len = 0x00010000;
-       const char alix_sig[] = "PC Engines ALIX.";
-       const size_t alix_sig_len = sizeof(alix_sig) - 1;
-
        const char *bios_virt;
        const char *scan_end;
        const char *p;
-       int ret = 0;
+       char name[64];
 
        if (force) {
                printk(KERN_NOTICE "%s: forced to skip BIOS test, "
                       "assume system has ALIX.2 style LEDs\n",
                       KBUILD_MODNAME);
-               ret = 1;
-               goto out;
+               return 1;
        }
 
        bios_virt = phys_to_virt(bios_phys);
        scan_end = bios_virt + bios_len - (alix_sig_len + 2);
        for (p = bios_virt; p < scan_end; p++) {
                const char *tail;
+               char *a;
 
-               if (memcmp(p, alix_sig, alix_sig_len) != 0) {
+               if (memcmp(p, alix_sig, alix_sig_len) != 0)
                        continue;
-               }
+
+               memcpy(name, p, sizeof(name));
+
+               /* remove the first \0 character from string */
+               a = strchr(name, '\0');
+               if (a)
+                       *a = ' ';
+
+               /* cut the string at a newline */
+               a = strchr(name, '\r');
+               if (a)
+                       *a = '\0';
 
                tail = p + alix_sig_len;
-               if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
+               if ((tail[0] == '2' || tail[0] == '3')) {
                        printk(KERN_INFO
                               "%s: system is recognized as \"%s\"\n",
-                              KBUILD_MODNAME, p);
-                       ret = 1;
-                       break;
+                              KBUILD_MODNAME, name);
+                       return 1;
                }
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static struct platform_device *pdev;
 
-static int __init alix_led_init(void)
+static int __init alix_pci_led_init(void)
 {
-       int ret;
+       u32 low, hi;
 
-       if (!alix_present()) {
-               ret = -ENODEV;
-               goto out;
+       if (pci_dev_present(divil_pci) == 0) {
+               printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
+               return -ENODEV;
        }
 
-       /* enable output on GPIO for LED 1,2,3 */
-       outl(1 << 6, 0x6104);
-       outl(1 << 9, 0x6184);
-       outl(1 << 11, 0x6184);
+       /* Grab the GPIO I/O range */
+       rdmsr(MSR_LBAR_GPIO, low, hi);
+
+       /* Check the mask and whether GPIO is enabled (sanity check) */
+       if (hi != 0x0000f001) {
+               printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
+               return -ENODEV;
+       }
+
+       /* Mask off the IO base address */
+       gpio_base = low & 0x0000ff00;
+
+       if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
+               printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
+               return -ENODEV;
+       }
+
+       /* Set GPIO function to output */
+       outl(1 << 6, gpio_base + 0x04);
+       outl(1 << 9, gpio_base + 0x84);
+       outl(1 << 11, gpio_base + 0x84);
+
+       return 0;
+}
+
+static int __init alix_led_init(void)
+{
+       int ret = -ENODEV;
+       const char tinybios_sig[] = "PC Engines ALIX.";
+       const char coreboot_sig[] = "PC Engines\0ALIX.";
+
+       if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
+           alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
+               ret = alix_pci_led_init();
+
+       if (ret < 0)
+               return ret;
 
        pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
        if (!IS_ERR(pdev)) {
@@ -168,7 +221,6 @@ static int __init alix_led_init(void)
        } else
                ret = PTR_ERR(pdev);
 
-out:
        return ret;
 }
 
@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void)
 {
        platform_device_unregister(pdev);
        platform_driver_unregister(&alix_led_driver);
+       release_region(gpio_base, CS5535_GPIO_SIZE);
 }
 
 module_init(alix_led_init);