Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>

authored by

Daniel Mack and committed by
Richard Purdie
7f131cf3 a328e95b

+84 -31
+84 -31
drivers/leds/leds-alix2.c
··· 11 11 #include <linux/module.h> 12 12 #include <linux/platform_device.h> 13 13 #include <linux/string.h> 14 + #include <linux/pci.h> 14 15 15 16 static int force = 0; 16 17 module_param(force, bool, 0444); 17 18 MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); 19 + 20 + #define MSR_LBAR_GPIO 0x5140000C 21 + #define CS5535_GPIO_SIZE 256 22 + 23 + static u32 gpio_base; 24 + 25 + static struct pci_device_id divil_pci[] = { 26 + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, 27 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, 28 + { } /* NULL entry */ 29 + }; 30 + MODULE_DEVICE_TABLE(pci, divil_pci); 18 31 19 32 struct alix_led { 20 33 struct led_classdev cdev; ··· 43 30 container_of(led_cdev, struct alix_led, cdev); 44 31 45 32 if (brightness) 46 - outl(led_dev->on_value, led_dev->port); 33 + outl(led_dev->on_value, gpio_base + led_dev->port); 47 34 else 48 - outl(led_dev->off_value, led_dev->port); 35 + outl(led_dev->off_value, gpio_base + led_dev->port); 49 36 } 50 37 51 38 static struct alix_led alix_leds[] = { ··· 54 41 .name = "alix:1", 55 42 .brightness_set = alix_led_set, 56 43 }, 57 - .port = 0x6100, 44 + .port = 0x00, 58 45 .on_value = 1 << 22, 59 46 .off_value = 1 << 6, 60 47 }, ··· 63 50 .name = "alix:2", 64 51 .brightness_set = alix_led_set, 65 52 }, 66 - .port = 0x6180, 53 + .port = 0x80, 67 54 .on_value = 1 << 25, 68 55 .off_value = 1 << 9, 69 56 }, ··· 72 59 .name = "alix:3", 73 60 .brightness_set = alix_led_set, 74 61 }, 75 - .port = 0x6180, 62 + .port = 0x80, 76 63 .on_value = 1 << 27, 77 64 .off_value = 1 << 11, 78 65 }, ··· 114 101 }, 115 102 }; 116 103 117 - static int __init alix_present(void) 104 + static int __init alix_present(unsigned long bios_phys, 105 + const char *alix_sig, 106 + size_t alix_sig_len) 118 107 { 119 - const unsigned long bios_phys = 0x000f0000; 120 108 const size_t bios_len = 0x00010000; 121 - const char alix_sig[] = "PC Engines ALIX."; 122 - const size_t alix_sig_len = sizeof(alix_sig) - 1; 123 - 124 109 const char *bios_virt; 125 110 const char *scan_end; 126 111 const char *p; 127 - int ret = 0; 112 + char name[64]; 128 113 129 114 if (force) { 130 115 printk(KERN_NOTICE "%s: forced to skip BIOS test, " 131 116 "assume system has ALIX.2 style LEDs\n", 132 117 KBUILD_MODNAME); 133 - ret = 1; 134 - goto out; 118 + return 1; 135 119 } 136 120 137 121 bios_virt = phys_to_virt(bios_phys); 138 122 scan_end = bios_virt + bios_len - (alix_sig_len + 2); 139 123 for (p = bios_virt; p < scan_end; p++) { 140 124 const char *tail; 125 + char *a; 141 126 142 - if (memcmp(p, alix_sig, alix_sig_len) != 0) { 127 + if (memcmp(p, alix_sig, alix_sig_len) != 0) 143 128 continue; 144 - } 129 + 130 + memcpy(name, p, sizeof(name)); 131 + 132 + /* remove the first \0 character from string */ 133 + a = strchr(name, '\0'); 134 + if (a) 135 + *a = ' '; 136 + 137 + /* cut the string at a newline */ 138 + a = strchr(name, '\r'); 139 + if (a) 140 + *a = '\0'; 145 141 146 142 tail = p + alix_sig_len; 147 - if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { 143 + if ((tail[0] == '2' || tail[0] == '3')) { 148 144 printk(KERN_INFO 149 145 "%s: system is recognized as \"%s\"\n", 150 - KBUILD_MODNAME, p); 151 - ret = 1; 152 - break; 146 + KBUILD_MODNAME, name); 147 + return 1; 153 148 } 154 149 } 155 150 156 - out: 157 - return ret; 151 + return 0; 158 152 } 159 153 160 154 static struct platform_device *pdev; 161 155 162 - static int __init alix_led_init(void) 156 + static int __init alix_pci_led_init(void) 163 157 { 164 - int ret; 158 + u32 low, hi; 165 159 166 - if (!alix_present()) { 167 - ret = -ENODEV; 168 - goto out; 160 + if (pci_dev_present(divil_pci) == 0) { 161 + printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); 162 + return -ENODEV; 169 163 } 170 164 171 - /* enable output on GPIO for LED 1,2,3 */ 172 - outl(1 << 6, 0x6104); 173 - outl(1 << 9, 0x6184); 174 - outl(1 << 11, 0x6184); 165 + /* Grab the GPIO I/O range */ 166 + rdmsr(MSR_LBAR_GPIO, low, hi); 167 + 168 + /* Check the mask and whether GPIO is enabled (sanity check) */ 169 + if (hi != 0x0000f001) { 170 + printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n"); 171 + return -ENODEV; 172 + } 173 + 174 + /* Mask off the IO base address */ 175 + gpio_base = low & 0x0000ff00; 176 + 177 + if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) { 178 + printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n"); 179 + return -ENODEV; 180 + } 181 + 182 + /* Set GPIO function to output */ 183 + outl(1 << 6, gpio_base + 0x04); 184 + outl(1 << 9, gpio_base + 0x84); 185 + outl(1 << 11, gpio_base + 0x84); 186 + 187 + return 0; 188 + } 189 + 190 + static int __init alix_led_init(void) 191 + { 192 + int ret = -ENODEV; 193 + const char tinybios_sig[] = "PC Engines ALIX."; 194 + const char coreboot_sig[] = "PC Engines\0ALIX."; 195 + 196 + if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || 197 + alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) 198 + ret = alix_pci_led_init(); 199 + 200 + if (ret < 0) 201 + return ret; 175 202 176 203 pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); 177 204 if (!IS_ERR(pdev)) { ··· 221 168 } else 222 169 ret = PTR_ERR(pdev); 223 170 224 - out: 225 171 return ret; 226 172 } 227 173 ··· 228 176 { 229 177 platform_device_unregister(pdev); 230 178 platform_driver_unregister(&alix_led_driver); 179 + release_region(gpio_base, CS5535_GPIO_SIZE); 231 180 } 232 181 233 182 module_init(alix_led_init);