| 1 | From: Ed Wildgoose <git@wildgooses.com> |
| 2 | Date: Wed, 3 Aug 2011 00:52:36 +0000 (+1000) |
| 3 | Subject: This new driver replaces the old PCEngines Alix 2/3 LED driver with a new |
| 4 | X-Git-Tag: next-20110812~1^2~75 |
| 5 | X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fnext%2Flinux-next.git;a=commitdiff_plain;h=2cd7b3e8f8078c214c93a2c60cb845500c92df4a;hp=dc849eae35bbf651ab3ee459f5683e1ff780413d |
| 6 | |
| 7 | This new driver replaces the old PCEngines Alix 2/3 LED driver with a new |
| 8 | driver that controls the LEDs through the leds-gpio driver. The old |
| 9 | driver accessed GPIOs directly, which created a conflict and prevented |
| 10 | also loading the cs5535-gpio driver to read other GPIOs on the Alix board. |
| 11 | With this new driver, we hook into leds-gpio which in turn uses GPIO to |
| 12 | control the LEDs and therefore it's possible to control both the LEDs and |
| 13 | access onboard GPIOs |
| 14 | |
| 15 | Driver is moved to platform/geode and any other geode initialisation |
| 16 | modules should move here also. |
| 17 | |
| 18 | This driver is inspired by leds-net5501.c by Alessandro Zummo. |
| 19 | |
| 20 | Ideally, leds-net5501.c should also be moved to platform/geode. |
| 21 | Additionally the driver relies on parts of the patch: 7f131cf3ed ("leds: |
| 22 | leds-alix2c - take port address from MSR) by Daniel Mack to perform |
| 23 | detection of the Alix board. |
| 24 | |
| 25 | Signed-off-by: Ed Wildgoose <kernel@wildgooses.com> |
| 26 | Cc: Alessandro Zummo <a.zummo@towertech.it> |
| 27 | Cc: Daniel Mack <daniel@caiaq.de> |
| 28 | Reviewed-by: Grant Likely <grant.likely@secretlab.ca> |
| 29 | Cc: Ingo Molnar <mingo@elte.hu> |
| 30 | Cc: Thomas Gleixner <tglx@linutronix.de> |
| 31 | Cc: "H. Peter Anvin" <hpa@zytor.com> |
| 32 | Cc: Richard Purdie <rpurdie@rpsys.net> |
| 33 | Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| 34 | --- |
| 35 | |
| 36 | --- a/arch/x86/Kconfig |
| 37 | +++ b/arch/x86/Kconfig |
| 38 | @@ -2087,6 +2087,20 @@ config OLPC_XO1 |
| 39 | ---help--- |
| 40 | Add support for non-essential features of the OLPC XO-1 laptop. |
| 41 | |
| 42 | +config ALIX |
| 43 | + bool "PCEngines ALIX System Support (LED setup)" |
| 44 | + select GPIOLIB |
| 45 | + ---help--- |
| 46 | + This option enables system support for the PCEngines ALIX. |
| 47 | + At present this just sets up LEDs for GPIO control on |
| 48 | + ALIX2/3/6 boards. However, other system specific setup should |
| 49 | + get added here. |
| 50 | + |
| 51 | + Note: You must still enable the drivers for GPIO and LED support |
| 52 | + (GPIO_CS5535 & LEDS_GPIO) to actually use the LEDs |
| 53 | + |
| 54 | + Note: You have to set alix.force=1 for boards with Award BIOS. |
| 55 | + |
| 56 | endif # X86_32 |
| 57 | |
| 58 | config AMD_NB |
| 59 | --- a/arch/x86/platform/Makefile |
| 60 | +++ b/arch/x86/platform/Makefile |
| 61 | @@ -1,6 +1,7 @@ |
| 62 | # Platform specific code goes here |
| 63 | obj-y += ce4100/ |
| 64 | obj-y += efi/ |
| 65 | +obj-y += geode/ |
| 66 | obj-y += iris/ |
| 67 | obj-y += mrst/ |
| 68 | obj-y += olpc/ |
| 69 | --- /dev/null |
| 70 | +++ b/arch/x86/platform/geode/Makefile |
| 71 | @@ -0,0 +1 @@ |
| 72 | +obj-$(CONFIG_ALIX) += alix.o |
| 73 | --- /dev/null |
| 74 | +++ b/arch/x86/platform/geode/alix.c |
| 75 | @@ -0,0 +1,141 @@ |
| 76 | +/* |
| 77 | + * System Specific setup for PCEngines ALIX. |
| 78 | + * At the moment this means setup of GPIO control of LEDs |
| 79 | + * on Alix.2/3/6 boards. |
| 80 | + * |
| 81 | + * |
| 82 | + * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> |
| 83 | + * Copyright (C) 2011 Ed Wildgoose <kernel@wildgooses.com> |
| 84 | + * |
| 85 | + * TODO: There are large similarities with leds-net5501.c |
| 86 | + * by Alessandro Zummo <a.zummo@towertech.it> |
| 87 | + * In the future leds-net5501.c should be migrated over to platform |
| 88 | + * |
| 89 | + * This program is free software; you can redistribute it and/or modify |
| 90 | + * it under the terms of the GNU General Public License version 2 |
| 91 | + * as published by the Free Software Foundation. |
| 92 | + */ |
| 93 | + |
| 94 | +#include <linux/kernel.h> |
| 95 | +#include <linux/init.h> |
| 96 | +#include <linux/io.h> |
| 97 | +#include <linux/string.h> |
| 98 | +#include <linux/leds.h> |
| 99 | +#include <linux/platform_device.h> |
| 100 | +#include <linux/gpio.h> |
| 101 | + |
| 102 | +#include <asm/geode.h> |
| 103 | + |
| 104 | +static int force = 0; |
| 105 | +module_param(force, bool, 0444); |
| 106 | +/* FIXME: Award bios is not automatically detected as Alix platform */ |
| 107 | +MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); |
| 108 | + |
| 109 | +static struct gpio_led alix_leds[] = { |
| 110 | + { |
| 111 | + .name = "alix:1", |
| 112 | + .gpio = 6, |
| 113 | + .default_trigger = "default-on", |
| 114 | + .active_low = 1, |
| 115 | + }, |
| 116 | + { |
| 117 | + .name = "alix:2", |
| 118 | + .gpio = 25, |
| 119 | + .default_trigger = "default-off", |
| 120 | + .active_low = 1, |
| 121 | + }, |
| 122 | + { |
| 123 | + .name = "alix:3", |
| 124 | + .gpio = 27, |
| 125 | + .default_trigger = "default-off", |
| 126 | + .active_low = 1, |
| 127 | + }, |
| 128 | +}; |
| 129 | + |
| 130 | +static struct gpio_led_platform_data alix_leds_data = { |
| 131 | + .num_leds = ARRAY_SIZE(alix_leds), |
| 132 | + .leds = alix_leds, |
| 133 | +}; |
| 134 | + |
| 135 | +static struct platform_device alix_leds_dev = { |
| 136 | + .name = "leds-gpio", |
| 137 | + .id = -1, |
| 138 | + .dev.platform_data = &alix_leds_data, |
| 139 | +}; |
| 140 | + |
| 141 | +static void __init register_alix(void) |
| 142 | +{ |
| 143 | + /* Setup LED control through leds-gpio driver */ |
| 144 | + platform_device_register(&alix_leds_dev); |
| 145 | +} |
| 146 | + |
| 147 | +static int __init alix_present(unsigned long bios_phys, |
| 148 | + const char *alix_sig, |
| 149 | + size_t alix_sig_len) |
| 150 | +{ |
| 151 | + const size_t bios_len = 0x00010000; |
| 152 | + const char *bios_virt; |
| 153 | + const char *scan_end; |
| 154 | + const char *p; |
| 155 | + char name[64]; |
| 156 | + |
| 157 | + if (force) { |
| 158 | + printk(KERN_NOTICE "%s: forced to skip BIOS test, " |
| 159 | + "assume system is ALIX.2/ALIX.3\n", |
| 160 | + KBUILD_MODNAME); |
| 161 | + return 1; |
| 162 | + } |
| 163 | + |
| 164 | + bios_virt = phys_to_virt(bios_phys); |
| 165 | + scan_end = bios_virt + bios_len - (alix_sig_len + 2); |
| 166 | + for (p = bios_virt; p < scan_end; p++) { |
| 167 | + const char *tail; |
| 168 | + char *a; |
| 169 | + |
| 170 | + if (memcmp(p, alix_sig, alix_sig_len) != 0) |
| 171 | + continue; |
| 172 | + |
| 173 | + memcpy(name, p, sizeof(name)); |
| 174 | + |
| 175 | + /* remove the first \0 character from string */ |
| 176 | + a = strchr(name, '\0'); |
| 177 | + if (a) |
| 178 | + *a = ' '; |
| 179 | + |
| 180 | + /* cut the string at a newline */ |
| 181 | + a = strchr(name, '\r'); |
| 182 | + if (a) |
| 183 | + *a = '\0'; |
| 184 | + |
| 185 | + tail = p + alix_sig_len; |
| 186 | + if ((tail[0] == '2' || tail[0] == '3')) { |
| 187 | + printk(KERN_INFO |
| 188 | + "%s: system is recognized as \"%s\"\n", |
| 189 | + KBUILD_MODNAME, name); |
| 190 | + return 1; |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + return 0; |
| 195 | +} |
| 196 | + |
| 197 | +static int __init alix_init(void) |
| 198 | +{ |
| 199 | + const char tinybios_sig[] = "PC Engines ALIX."; |
| 200 | + const char coreboot_sig[] = "PC Engines\0ALIX."; |
| 201 | + |
| 202 | + if (!is_geode()) |
| 203 | + return 0; |
| 204 | + |
| 205 | + if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || |
| 206 | + alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) |
| 207 | + register_alix(); |
| 208 | + |
| 209 | + return 0; |
| 210 | +} |
| 211 | + |
| 212 | +module_init(alix_init); |
| 213 | + |
| 214 | +MODULE_AUTHOR("Ed Wildgoose <kernel@wildgooses.com>"); |
| 215 | +MODULE_DESCRIPTION("PCEngines ALIX System Setup"); |
| 216 | +MODULE_LICENSE("GPL"); |
| 217 | --- a/drivers/leds/Kconfig |
| 218 | +++ b/drivers/leds/Kconfig |
| 219 | @@ -107,14 +107,6 @@ config LEDS_WRAP |
| 220 | help |
| 221 | This option enables support for the PCEngines WRAP programmable LEDs. |
| 222 | |
| 223 | -config LEDS_ALIX2 |
| 224 | - tristate "LED Support for ALIX.2 and ALIX.3 series" |
| 225 | - depends on LEDS_CLASS |
| 226 | - depends on X86 && !GPIO_CS5535 && !CS5535_GPIO |
| 227 | - help |
| 228 | - This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. |
| 229 | - You have to set leds-alix2.force=1 for boards with Award BIOS. |
| 230 | - |
| 231 | config LEDS_H1940 |
| 232 | tristate "LED Support for iPAQ H1940 device" |
| 233 | depends on LEDS_CLASS |
| 234 | --- a/drivers/leds/Makefile |
| 235 | +++ b/drivers/leds/Makefile |
| 236 | @@ -16,7 +16,6 @@ obj-$(CONFIG_LEDS_AMS_DELTA) += leds-am |
| 237 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o |
| 238 | obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o |
| 239 | obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o |
| 240 | -obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o |
| 241 | obj-$(CONFIG_LEDS_H1940) += leds-h1940.o |
| 242 | obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o |
| 243 | obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o |
| 244 | --- a/drivers/leds/leds-alix2.c |
| 245 | +++ /dev/null |
| 246 | @@ -1,239 +0,0 @@ |
| 247 | -/* |
| 248 | - * LEDs driver for PCEngines ALIX.2 and ALIX.3 |
| 249 | - * |
| 250 | - * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> |
| 251 | - */ |
| 252 | - |
| 253 | -#include <linux/err.h> |
| 254 | -#include <linux/io.h> |
| 255 | -#include <linux/kernel.h> |
| 256 | -#include <linux/leds.h> |
| 257 | -#include <linux/module.h> |
| 258 | -#include <linux/platform_device.h> |
| 259 | -#include <linux/string.h> |
| 260 | -#include <linux/pci.h> |
| 261 | - |
| 262 | -static int force = 0; |
| 263 | -module_param(force, bool, 0444); |
| 264 | -MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); |
| 265 | - |
| 266 | -#define MSR_LBAR_GPIO 0x5140000C |
| 267 | -#define CS5535_GPIO_SIZE 256 |
| 268 | - |
| 269 | -static u32 gpio_base; |
| 270 | - |
| 271 | -static struct pci_device_id divil_pci[] = { |
| 272 | - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, |
| 273 | - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, |
| 274 | - { } /* NULL entry */ |
| 275 | -}; |
| 276 | -MODULE_DEVICE_TABLE(pci, divil_pci); |
| 277 | - |
| 278 | -struct alix_led { |
| 279 | - struct led_classdev cdev; |
| 280 | - unsigned short port; |
| 281 | - unsigned int on_value; |
| 282 | - unsigned int off_value; |
| 283 | -}; |
| 284 | - |
| 285 | -static void alix_led_set(struct led_classdev *led_cdev, |
| 286 | - enum led_brightness brightness) |
| 287 | -{ |
| 288 | - struct alix_led *led_dev = |
| 289 | - container_of(led_cdev, struct alix_led, cdev); |
| 290 | - |
| 291 | - if (brightness) |
| 292 | - outl(led_dev->on_value, gpio_base + led_dev->port); |
| 293 | - else |
| 294 | - outl(led_dev->off_value, gpio_base + led_dev->port); |
| 295 | -} |
| 296 | - |
| 297 | -static struct alix_led alix_leds[] = { |
| 298 | - { |
| 299 | - .cdev = { |
| 300 | - .name = "alix:1", |
| 301 | - .brightness_set = alix_led_set, |
| 302 | - }, |
| 303 | - .port = 0x00, |
| 304 | - .on_value = 1 << 22, |
| 305 | - .off_value = 1 << 6, |
| 306 | - }, |
| 307 | - { |
| 308 | - .cdev = { |
| 309 | - .name = "alix:2", |
| 310 | - .brightness_set = alix_led_set, |
| 311 | - }, |
| 312 | - .port = 0x80, |
| 313 | - .on_value = 1 << 25, |
| 314 | - .off_value = 1 << 9, |
| 315 | - }, |
| 316 | - { |
| 317 | - .cdev = { |
| 318 | - .name = "alix:3", |
| 319 | - .brightness_set = alix_led_set, |
| 320 | - }, |
| 321 | - .port = 0x80, |
| 322 | - .on_value = 1 << 27, |
| 323 | - .off_value = 1 << 11, |
| 324 | - }, |
| 325 | -}; |
| 326 | - |
| 327 | -static int __init alix_led_probe(struct platform_device *pdev) |
| 328 | -{ |
| 329 | - int i; |
| 330 | - int ret; |
| 331 | - |
| 332 | - for (i = 0; i < ARRAY_SIZE(alix_leds); i++) { |
| 333 | - alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; |
| 334 | - ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev); |
| 335 | - if (ret < 0) |
| 336 | - goto fail; |
| 337 | - } |
| 338 | - return 0; |
| 339 | - |
| 340 | -fail: |
| 341 | - while (--i >= 0) |
| 342 | - led_classdev_unregister(&alix_leds[i].cdev); |
| 343 | - return ret; |
| 344 | -} |
| 345 | - |
| 346 | -static int alix_led_remove(struct platform_device *pdev) |
| 347 | -{ |
| 348 | - int i; |
| 349 | - |
| 350 | - for (i = 0; i < ARRAY_SIZE(alix_leds); i++) |
| 351 | - led_classdev_unregister(&alix_leds[i].cdev); |
| 352 | - return 0; |
| 353 | -} |
| 354 | - |
| 355 | -static struct platform_driver alix_led_driver = { |
| 356 | - .remove = alix_led_remove, |
| 357 | - .driver = { |
| 358 | - .name = KBUILD_MODNAME, |
| 359 | - .owner = THIS_MODULE, |
| 360 | - }, |
| 361 | -}; |
| 362 | - |
| 363 | -static int __init alix_present(unsigned long bios_phys, |
| 364 | - const char *alix_sig, |
| 365 | - size_t alix_sig_len) |
| 366 | -{ |
| 367 | - const size_t bios_len = 0x00010000; |
| 368 | - const char *bios_virt; |
| 369 | - const char *scan_end; |
| 370 | - const char *p; |
| 371 | - char name[64]; |
| 372 | - |
| 373 | - if (force) { |
| 374 | - printk(KERN_NOTICE "%s: forced to skip BIOS test, " |
| 375 | - "assume system has ALIX.2 style LEDs\n", |
| 376 | - KBUILD_MODNAME); |
| 377 | - return 1; |
| 378 | - } |
| 379 | - |
| 380 | - bios_virt = phys_to_virt(bios_phys); |
| 381 | - scan_end = bios_virt + bios_len - (alix_sig_len + 2); |
| 382 | - for (p = bios_virt; p < scan_end; p++) { |
| 383 | - const char *tail; |
| 384 | - char *a; |
| 385 | - |
| 386 | - if (memcmp(p, alix_sig, alix_sig_len) != 0) |
| 387 | - continue; |
| 388 | - |
| 389 | - memcpy(name, p, sizeof(name)); |
| 390 | - |
| 391 | - /* remove the first \0 character from string */ |
| 392 | - a = strchr(name, '\0'); |
| 393 | - if (a) |
| 394 | - *a = ' '; |
| 395 | - |
| 396 | - /* cut the string at a newline */ |
| 397 | - a = strchr(name, '\r'); |
| 398 | - if (a) |
| 399 | - *a = '\0'; |
| 400 | - |
| 401 | - tail = p + alix_sig_len; |
| 402 | - if ((tail[0] == '2' || tail[0] == '3')) { |
| 403 | - printk(KERN_INFO |
| 404 | - "%s: system is recognized as \"%s\"\n", |
| 405 | - KBUILD_MODNAME, name); |
| 406 | - return 1; |
| 407 | - } |
| 408 | - } |
| 409 | - |
| 410 | - return 0; |
| 411 | -} |
| 412 | - |
| 413 | -static struct platform_device *pdev; |
| 414 | - |
| 415 | -static int __init alix_pci_led_init(void) |
| 416 | -{ |
| 417 | - u32 low, hi; |
| 418 | - |
| 419 | - if (pci_dev_present(divil_pci) == 0) { |
| 420 | - printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); |
| 421 | - return -ENODEV; |
| 422 | - } |
| 423 | - |
| 424 | - /* Grab the GPIO I/O range */ |
| 425 | - rdmsr(MSR_LBAR_GPIO, low, hi); |
| 426 | - |
| 427 | - /* Check the mask and whether GPIO is enabled (sanity check) */ |
| 428 | - if (hi != 0x0000f001) { |
| 429 | - printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n"); |
| 430 | - return -ENODEV; |
| 431 | - } |
| 432 | - |
| 433 | - /* Mask off the IO base address */ |
| 434 | - gpio_base = low & 0x0000ff00; |
| 435 | - |
| 436 | - if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) { |
| 437 | - printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n"); |
| 438 | - return -ENODEV; |
| 439 | - } |
| 440 | - |
| 441 | - /* Set GPIO function to output */ |
| 442 | - outl(1 << 6, gpio_base + 0x04); |
| 443 | - outl(1 << 9, gpio_base + 0x84); |
| 444 | - outl(1 << 11, gpio_base + 0x84); |
| 445 | - |
| 446 | - return 0; |
| 447 | -} |
| 448 | - |
| 449 | -static int __init alix_led_init(void) |
| 450 | -{ |
| 451 | - int ret = -ENODEV; |
| 452 | - const char tinybios_sig[] = "PC Engines ALIX."; |
| 453 | - const char coreboot_sig[] = "PC Engines\0ALIX."; |
| 454 | - |
| 455 | - if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || |
| 456 | - alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) |
| 457 | - ret = alix_pci_led_init(); |
| 458 | - |
| 459 | - if (ret < 0) |
| 460 | - return ret; |
| 461 | - |
| 462 | - pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); |
| 463 | - if (!IS_ERR(pdev)) { |
| 464 | - ret = platform_driver_probe(&alix_led_driver, alix_led_probe); |
| 465 | - if (ret) |
| 466 | - platform_device_unregister(pdev); |
| 467 | - } else |
| 468 | - ret = PTR_ERR(pdev); |
| 469 | - |
| 470 | - return ret; |
| 471 | -} |
| 472 | - |
| 473 | -static void __exit alix_led_exit(void) |
| 474 | -{ |
| 475 | - platform_device_unregister(pdev); |
| 476 | - platform_driver_unregister(&alix_led_driver); |
| 477 | - release_region(gpio_base, CS5535_GPIO_SIZE); |
| 478 | -} |
| 479 | - |
| 480 | -module_init(alix_led_init); |
| 481 | -module_exit(alix_led_exit); |
| 482 | - |
| 483 | -MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>"); |
| 484 | -MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver"); |
| 485 | -MODULE_LICENSE("GPL"); |
| 486 | |