Commit 7f131cf3 authored by Daniel Mack's avatar Daniel Mack Committed by Richard Purdie

leds: leds-alix2c - take port address from MSR

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: default avatarDaniel Mack <daniel@caiaq.de>
Signed-off-by: default avatarRichard Purdie <rpurdie@linux.intel.com>
parent a328e95b
......@@ -11,11 +11,24 @@
#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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment