Commit d4f3e350172a1dc769ed5e7f5bd540feb0c475d8
Committed by
Thomas Gleixner
1 parent
9d037a7776
Exists in
master
and in
20 other branches
x86: geode: New PCEngines Alix system driver
This new driver replaces the old PCEngines Alix 2/3 LED driver with a new driver that controls the LEDs through the leds-gpio driver. The old driver accessed GPIOs directly, which created a conflict and prevented also loading the cs5535-gpio driver to read other GPIOs on the Alix board. With this new driver, we hook into leds-gpio which in turn uses GPIO to control the LEDs and therefore it's possible to control both the LEDs and access onboard GPIOs Driver is moved to platform/geode as requested by Grant and any other geode initialisation modules should move here also This driver is inspired by leds-net5501.c by Alessandro Zummo. Ideally, leds-net5501.c should also be moved to platform/geode. Additionally the driver relies on parts of the patch: 7f131cf3ed ("leds: leds-alix2c - take port address from MSR) by Daniel Mack to perform detection of the Alix board. [akpm@linux-foundation.org: include module.h] Signed-off-by: Ed Wildgoose <kernel@wildgooses.com> Cc: git@wildgooses.com Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Daniel Mack <daniel@caiaq.de> Cc: Ingo Molnar <mingo@elte.hu> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Richard Purdie <rpurdie@rpsys.net> Reviewed-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Showing 7 changed files with 158 additions and 248 deletions Side-by-side Diff
arch/x86/Kconfig
... | ... | @@ -2064,6 +2064,20 @@ |
2064 | 2064 | - AC adapter status updates |
2065 | 2065 | - Battery status updates |
2066 | 2066 | |
2067 | +config ALIX | |
2068 | + bool "PCEngines ALIX System Support (LED setup)" | |
2069 | + select GPIOLIB | |
2070 | + ---help--- | |
2071 | + This option enables system support for the PCEngines ALIX. | |
2072 | + At present this just sets up LEDs for GPIO control on | |
2073 | + ALIX2/3/6 boards. However, other system specific setup should | |
2074 | + get added here. | |
2075 | + | |
2076 | + Note: You must still enable the drivers for GPIO and LED support | |
2077 | + (GPIO_CS5535 & LEDS_GPIO) to actually use the LEDs | |
2078 | + | |
2079 | + Note: You have to set alix.force=1 for boards with Award BIOS. | |
2080 | + | |
2067 | 2081 | endif # X86_32 |
2068 | 2082 | |
2069 | 2083 | config AMD_NB |
arch/x86/platform/Makefile
arch/x86/platform/geode/Makefile
1 | +obj-$(CONFIG_ALIX) += alix.o |
arch/x86/platform/geode/alix.c
1 | +/* | |
2 | + * System Specific setup for PCEngines ALIX. | |
3 | + * At the moment this means setup of GPIO control of LEDs | |
4 | + * on Alix.2/3/6 boards. | |
5 | + * | |
6 | + * | |
7 | + * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> | |
8 | + * Copyright (C) 2011 Ed Wildgoose <kernel@wildgooses.com> | |
9 | + * | |
10 | + * TODO: There are large similarities with leds-net5501.c | |
11 | + * by Alessandro Zummo <a.zummo@towertech.it> | |
12 | + * In the future leds-net5501.c should be migrated over to platform | |
13 | + * | |
14 | + * This program is free software; you can redistribute it and/or modify | |
15 | + * it under the terms of the GNU General Public License version 2 | |
16 | + * as published by the Free Software Foundation. | |
17 | + */ | |
18 | + | |
19 | +#include <linux/kernel.h> | |
20 | +#include <linux/init.h> | |
21 | +#include <linux/io.h> | |
22 | +#include <linux/string.h> | |
23 | +#include <linux/module.h> | |
24 | +#include <linux/leds.h> | |
25 | +#include <linux/platform_device.h> | |
26 | +#include <linux/gpio.h> | |
27 | + | |
28 | +#include <asm/geode.h> | |
29 | + | |
30 | +static int force = 0; | |
31 | +module_param(force, bool, 0444); | |
32 | +/* FIXME: Award bios is not automatically detected as Alix platform */ | |
33 | +MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); | |
34 | + | |
35 | +static struct gpio_led alix_leds[] = { | |
36 | + { | |
37 | + .name = "alix:1", | |
38 | + .gpio = 6, | |
39 | + .default_trigger = "default-on", | |
40 | + .active_low = 1, | |
41 | + }, | |
42 | + { | |
43 | + .name = "alix:2", | |
44 | + .gpio = 25, | |
45 | + .default_trigger = "default-off", | |
46 | + .active_low = 1, | |
47 | + }, | |
48 | + { | |
49 | + .name = "alix:3", | |
50 | + .gpio = 27, | |
51 | + .default_trigger = "default-off", | |
52 | + .active_low = 1, | |
53 | + }, | |
54 | +}; | |
55 | + | |
56 | +static struct gpio_led_platform_data alix_leds_data = { | |
57 | + .num_leds = ARRAY_SIZE(alix_leds), | |
58 | + .leds = alix_leds, | |
59 | +}; | |
60 | + | |
61 | +static struct platform_device alix_leds_dev = { | |
62 | + .name = "leds-gpio", | |
63 | + .id = -1, | |
64 | + .dev.platform_data = &alix_leds_data, | |
65 | +}; | |
66 | + | |
67 | +static void __init register_alix(void) | |
68 | +{ | |
69 | + /* Setup LED control through leds-gpio driver */ | |
70 | + platform_device_register(&alix_leds_dev); | |
71 | +} | |
72 | + | |
73 | +static int __init alix_present(unsigned long bios_phys, | |
74 | + const char *alix_sig, | |
75 | + size_t alix_sig_len) | |
76 | +{ | |
77 | + const size_t bios_len = 0x00010000; | |
78 | + const char *bios_virt; | |
79 | + const char *scan_end; | |
80 | + const char *p; | |
81 | + char name[64]; | |
82 | + | |
83 | + if (force) { | |
84 | + printk(KERN_NOTICE "%s: forced to skip BIOS test, " | |
85 | + "assume system is ALIX.2/ALIX.3\n", | |
86 | + KBUILD_MODNAME); | |
87 | + return 1; | |
88 | + } | |
89 | + | |
90 | + bios_virt = phys_to_virt(bios_phys); | |
91 | + scan_end = bios_virt + bios_len - (alix_sig_len + 2); | |
92 | + for (p = bios_virt; p < scan_end; p++) { | |
93 | + const char *tail; | |
94 | + char *a; | |
95 | + | |
96 | + if (memcmp(p, alix_sig, alix_sig_len) != 0) | |
97 | + continue; | |
98 | + | |
99 | + memcpy(name, p, sizeof(name)); | |
100 | + | |
101 | + /* remove the first \0 character from string */ | |
102 | + a = strchr(name, '\0'); | |
103 | + if (a) | |
104 | + *a = ' '; | |
105 | + | |
106 | + /* cut the string at a newline */ | |
107 | + a = strchr(name, '\r'); | |
108 | + if (a) | |
109 | + *a = '\0'; | |
110 | + | |
111 | + tail = p + alix_sig_len; | |
112 | + if ((tail[0] == '2' || tail[0] == '3')) { | |
113 | + printk(KERN_INFO | |
114 | + "%s: system is recognized as \"%s\"\n", | |
115 | + KBUILD_MODNAME, name); | |
116 | + return 1; | |
117 | + } | |
118 | + } | |
119 | + | |
120 | + return 0; | |
121 | +} | |
122 | + | |
123 | +static int __init alix_init(void) | |
124 | +{ | |
125 | + const char tinybios_sig[] = "PC Engines ALIX."; | |
126 | + const char coreboot_sig[] = "PC Engines\0ALIX."; | |
127 | + | |
128 | + if (!is_geode()) | |
129 | + return 0; | |
130 | + | |
131 | + if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || | |
132 | + alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) | |
133 | + register_alix(); | |
134 | + | |
135 | + return 0; | |
136 | +} | |
137 | + | |
138 | +module_init(alix_init); | |
139 | + | |
140 | +MODULE_AUTHOR("Ed Wildgoose <kernel@wildgooses.com>"); | |
141 | +MODULE_DESCRIPTION("PCEngines ALIX System Setup"); | |
142 | +MODULE_LICENSE("GPL"); |
drivers/leds/Kconfig
... | ... | @@ -113,14 +113,6 @@ |
113 | 113 | help |
114 | 114 | This option enables support for the PCEngines WRAP programmable LEDs. |
115 | 115 | |
116 | -config LEDS_ALIX2 | |
117 | - tristate "LED Support for ALIX.2 and ALIX.3 series" | |
118 | - depends on LEDS_CLASS | |
119 | - depends on X86 && !GPIO_CS5535 && !CS5535_GPIO | |
120 | - help | |
121 | - This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. | |
122 | - You have to set leds-alix2.force=1 for boards with Award BIOS. | |
123 | - | |
124 | 116 | config LEDS_COBALT_QUBE |
125 | 117 | tristate "LED Support for the Cobalt Qube series front LED" |
126 | 118 | depends on LEDS_CLASS |
drivers/leds/Makefile
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o |
17 | 17 | obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o |
18 | 18 | obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o |
19 | -obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o | |
20 | 19 | obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o |
21 | 20 | obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o |
22 | 21 | obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o |
drivers/leds/leds-alix2.c
1 | -/* | |
2 | - * LEDs driver for PCEngines ALIX.2 and ALIX.3 | |
3 | - * | |
4 | - * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> | |
5 | - */ | |
6 | - | |
7 | -#include <linux/err.h> | |
8 | -#include <linux/io.h> | |
9 | -#include <linux/kernel.h> | |
10 | -#include <linux/leds.h> | |
11 | -#include <linux/module.h> | |
12 | -#include <linux/platform_device.h> | |
13 | -#include <linux/string.h> | |
14 | -#include <linux/pci.h> | |
15 | - | |
16 | -static int force = 0; | |
17 | -module_param(force, bool, 0444); | |
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); | |
31 | - | |
32 | -struct alix_led { | |
33 | - struct led_classdev cdev; | |
34 | - unsigned short port; | |
35 | - unsigned int on_value; | |
36 | - unsigned int off_value; | |
37 | -}; | |
38 | - | |
39 | -static void alix_led_set(struct led_classdev *led_cdev, | |
40 | - enum led_brightness brightness) | |
41 | -{ | |
42 | - struct alix_led *led_dev = | |
43 | - container_of(led_cdev, struct alix_led, cdev); | |
44 | - | |
45 | - if (brightness) | |
46 | - outl(led_dev->on_value, gpio_base + led_dev->port); | |
47 | - else | |
48 | - outl(led_dev->off_value, gpio_base + led_dev->port); | |
49 | -} | |
50 | - | |
51 | -static struct alix_led alix_leds[] = { | |
52 | - { | |
53 | - .cdev = { | |
54 | - .name = "alix:1", | |
55 | - .brightness_set = alix_led_set, | |
56 | - }, | |
57 | - .port = 0x00, | |
58 | - .on_value = 1 << 22, | |
59 | - .off_value = 1 << 6, | |
60 | - }, | |
61 | - { | |
62 | - .cdev = { | |
63 | - .name = "alix:2", | |
64 | - .brightness_set = alix_led_set, | |
65 | - }, | |
66 | - .port = 0x80, | |
67 | - .on_value = 1 << 25, | |
68 | - .off_value = 1 << 9, | |
69 | - }, | |
70 | - { | |
71 | - .cdev = { | |
72 | - .name = "alix:3", | |
73 | - .brightness_set = alix_led_set, | |
74 | - }, | |
75 | - .port = 0x80, | |
76 | - .on_value = 1 << 27, | |
77 | - .off_value = 1 << 11, | |
78 | - }, | |
79 | -}; | |
80 | - | |
81 | -static int __init alix_led_probe(struct platform_device *pdev) | |
82 | -{ | |
83 | - int i; | |
84 | - int ret; | |
85 | - | |
86 | - for (i = 0; i < ARRAY_SIZE(alix_leds); i++) { | |
87 | - alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; | |
88 | - ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev); | |
89 | - if (ret < 0) | |
90 | - goto fail; | |
91 | - } | |
92 | - return 0; | |
93 | - | |
94 | -fail: | |
95 | - while (--i >= 0) | |
96 | - led_classdev_unregister(&alix_leds[i].cdev); | |
97 | - return ret; | |
98 | -} | |
99 | - | |
100 | -static int alix_led_remove(struct platform_device *pdev) | |
101 | -{ | |
102 | - int i; | |
103 | - | |
104 | - for (i = 0; i < ARRAY_SIZE(alix_leds); i++) | |
105 | - led_classdev_unregister(&alix_leds[i].cdev); | |
106 | - return 0; | |
107 | -} | |
108 | - | |
109 | -static struct platform_driver alix_led_driver = { | |
110 | - .remove = alix_led_remove, | |
111 | - .driver = { | |
112 | - .name = KBUILD_MODNAME, | |
113 | - .owner = THIS_MODULE, | |
114 | - }, | |
115 | -}; | |
116 | - | |
117 | -static int __init alix_present(unsigned long bios_phys, | |
118 | - const char *alix_sig, | |
119 | - size_t alix_sig_len) | |
120 | -{ | |
121 | - const size_t bios_len = 0x00010000; | |
122 | - const char *bios_virt; | |
123 | - const char *scan_end; | |
124 | - const char *p; | |
125 | - char name[64]; | |
126 | - | |
127 | - if (force) { | |
128 | - printk(KERN_NOTICE "%s: forced to skip BIOS test, " | |
129 | - "assume system has ALIX.2 style LEDs\n", | |
130 | - KBUILD_MODNAME); | |
131 | - return 1; | |
132 | - } | |
133 | - | |
134 | - bios_virt = phys_to_virt(bios_phys); | |
135 | - scan_end = bios_virt + bios_len - (alix_sig_len + 2); | |
136 | - for (p = bios_virt; p < scan_end; p++) { | |
137 | - const char *tail; | |
138 | - char *a; | |
139 | - | |
140 | - if (memcmp(p, alix_sig, alix_sig_len) != 0) | |
141 | - continue; | |
142 | - | |
143 | - memcpy(name, p, sizeof(name)); | |
144 | - | |
145 | - /* remove the first \0 character from string */ | |
146 | - a = strchr(name, '\0'); | |
147 | - if (a) | |
148 | - *a = ' '; | |
149 | - | |
150 | - /* cut the string at a newline */ | |
151 | - a = strchr(name, '\r'); | |
152 | - if (a) | |
153 | - *a = '\0'; | |
154 | - | |
155 | - tail = p + alix_sig_len; | |
156 | - if ((tail[0] == '2' || tail[0] == '3')) { | |
157 | - printk(KERN_INFO | |
158 | - "%s: system is recognized as \"%s\"\n", | |
159 | - KBUILD_MODNAME, name); | |
160 | - return 1; | |
161 | - } | |
162 | - } | |
163 | - | |
164 | - return 0; | |
165 | -} | |
166 | - | |
167 | -static struct platform_device *pdev; | |
168 | - | |
169 | -static int __init alix_pci_led_init(void) | |
170 | -{ | |
171 | - u32 low, hi; | |
172 | - | |
173 | - if (pci_dev_present(divil_pci) == 0) { | |
174 | - printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); | |
175 | - return -ENODEV; | |
176 | - } | |
177 | - | |
178 | - /* Grab the GPIO I/O range */ | |
179 | - rdmsr(MSR_LBAR_GPIO, low, hi); | |
180 | - | |
181 | - /* Check the mask and whether GPIO is enabled (sanity check) */ | |
182 | - if (hi != 0x0000f001) { | |
183 | - printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n"); | |
184 | - return -ENODEV; | |
185 | - } | |
186 | - | |
187 | - /* Mask off the IO base address */ | |
188 | - gpio_base = low & 0x0000ff00; | |
189 | - | |
190 | - if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) { | |
191 | - printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n"); | |
192 | - return -ENODEV; | |
193 | - } | |
194 | - | |
195 | - /* Set GPIO function to output */ | |
196 | - outl(1 << 6, gpio_base + 0x04); | |
197 | - outl(1 << 9, gpio_base + 0x84); | |
198 | - outl(1 << 11, gpio_base + 0x84); | |
199 | - | |
200 | - return 0; | |
201 | -} | |
202 | - | |
203 | -static int __init alix_led_init(void) | |
204 | -{ | |
205 | - int ret = -ENODEV; | |
206 | - const char tinybios_sig[] = "PC Engines ALIX."; | |
207 | - const char coreboot_sig[] = "PC Engines\0ALIX."; | |
208 | - | |
209 | - if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || | |
210 | - alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) | |
211 | - ret = alix_pci_led_init(); | |
212 | - | |
213 | - if (ret < 0) | |
214 | - return ret; | |
215 | - | |
216 | - pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | |
217 | - if (!IS_ERR(pdev)) { | |
218 | - ret = platform_driver_probe(&alix_led_driver, alix_led_probe); | |
219 | - if (ret) | |
220 | - platform_device_unregister(pdev); | |
221 | - } else | |
222 | - ret = PTR_ERR(pdev); | |
223 | - | |
224 | - return ret; | |
225 | -} | |
226 | - | |
227 | -static void __exit alix_led_exit(void) | |
228 | -{ | |
229 | - platform_device_unregister(pdev); | |
230 | - platform_driver_unregister(&alix_led_driver); | |
231 | - release_region(gpio_base, CS5535_GPIO_SIZE); | |
232 | -} | |
233 | - | |
234 | -module_init(alix_led_init); | |
235 | -module_exit(alix_led_exit); | |
236 | - | |
237 | -MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>"); | |
238 | -MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver"); | |
239 | -MODULE_LICENSE("GPL"); |