diff options
Diffstat (limited to 'drivers/leds/leds-alix2.c')
-rw-r--r-- | drivers/leds/leds-alix2.c | 115 |
1 files changed, 84 insertions, 31 deletions
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c index 731d4eef3425..f59ffadf5125 100644 --- a/drivers/leds/leds-alix2.c +++ b/drivers/leds/leds-alix2.c | |||
@@ -11,11 +11,24 @@ | |||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
13 | #include <linux/string.h> | 13 | #include <linux/string.h> |
14 | #include <linux/pci.h> | ||
14 | 15 | ||
15 | static int force = 0; | 16 | static int force = 0; |
16 | module_param(force, bool, 0444); | 17 | module_param(force, bool, 0444); |
17 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); | 18 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); |
18 | 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 | |||
19 | struct alix_led { | 32 | struct alix_led { |
20 | struct led_classdev cdev; | 33 | struct led_classdev cdev; |
21 | unsigned short port; | 34 | unsigned short port; |
@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev, | |||
30 | container_of(led_cdev, struct alix_led, cdev); | 43 | container_of(led_cdev, struct alix_led, cdev); |
31 | 44 | ||
32 | if (brightness) | 45 | if (brightness) |
33 | outl(led_dev->on_value, led_dev->port); | 46 | outl(led_dev->on_value, gpio_base + led_dev->port); |
34 | else | 47 | else |
35 | outl(led_dev->off_value, led_dev->port); | 48 | outl(led_dev->off_value, gpio_base + led_dev->port); |
36 | } | 49 | } |
37 | 50 | ||
38 | static struct alix_led alix_leds[] = { | 51 | static struct alix_led alix_leds[] = { |
@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = { | |||
41 | .name = "alix:1", | 54 | .name = "alix:1", |
42 | .brightness_set = alix_led_set, | 55 | .brightness_set = alix_led_set, |
43 | }, | 56 | }, |
44 | .port = 0x6100, | 57 | .port = 0x00, |
45 | .on_value = 1 << 22, | 58 | .on_value = 1 << 22, |
46 | .off_value = 1 << 6, | 59 | .off_value = 1 << 6, |
47 | }, | 60 | }, |
@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = { | |||
50 | .name = "alix:2", | 63 | .name = "alix:2", |
51 | .brightness_set = alix_led_set, | 64 | .brightness_set = alix_led_set, |
52 | }, | 65 | }, |
53 | .port = 0x6180, | 66 | .port = 0x80, |
54 | .on_value = 1 << 25, | 67 | .on_value = 1 << 25, |
55 | .off_value = 1 << 9, | 68 | .off_value = 1 << 9, |
56 | }, | 69 | }, |
@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = { | |||
59 | .name = "alix:3", | 72 | .name = "alix:3", |
60 | .brightness_set = alix_led_set, | 73 | .brightness_set = alix_led_set, |
61 | }, | 74 | }, |
62 | .port = 0x6180, | 75 | .port = 0x80, |
63 | .on_value = 1 << 27, | 76 | .on_value = 1 << 27, |
64 | .off_value = 1 << 11, | 77 | .off_value = 1 << 11, |
65 | }, | 78 | }, |
@@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = { | |||
101 | }, | 114 | }, |
102 | }; | 115 | }; |
103 | 116 | ||
104 | static int __init alix_present(void) | 117 | static int __init alix_present(unsigned long bios_phys, |
118 | const char *alix_sig, | ||
119 | size_t alix_sig_len) | ||
105 | { | 120 | { |
106 | const unsigned long bios_phys = 0x000f0000; | ||
107 | const size_t bios_len = 0x00010000; | 121 | const size_t bios_len = 0x00010000; |
108 | const char alix_sig[] = "PC Engines ALIX."; | ||
109 | const size_t alix_sig_len = sizeof(alix_sig) - 1; | ||
110 | |||
111 | const char *bios_virt; | 122 | const char *bios_virt; |
112 | const char *scan_end; | 123 | const char *scan_end; |
113 | const char *p; | 124 | const char *p; |
114 | int ret = 0; | 125 | char name[64]; |
115 | 126 | ||
116 | if (force) { | 127 | if (force) { |
117 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " | 128 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " |
118 | "assume system has ALIX.2 style LEDs\n", | 129 | "assume system has ALIX.2 style LEDs\n", |
119 | KBUILD_MODNAME); | 130 | KBUILD_MODNAME); |
120 | ret = 1; | 131 | return 1; |
121 | goto out; | ||
122 | } | 132 | } |
123 | 133 | ||
124 | bios_virt = phys_to_virt(bios_phys); | 134 | bios_virt = phys_to_virt(bios_phys); |
125 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); | 135 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); |
126 | for (p = bios_virt; p < scan_end; p++) { | 136 | for (p = bios_virt; p < scan_end; p++) { |
127 | const char *tail; | 137 | const char *tail; |
138 | char *a; | ||
128 | 139 | ||
129 | if (memcmp(p, alix_sig, alix_sig_len) != 0) { | 140 | if (memcmp(p, alix_sig, alix_sig_len) != 0) |
130 | continue; | 141 | continue; |
131 | } | 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'; | ||
132 | 154 | ||
133 | tail = p + alix_sig_len; | 155 | tail = p + alix_sig_len; |
134 | if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { | 156 | if ((tail[0] == '2' || tail[0] == '3')) { |
135 | printk(KERN_INFO | 157 | printk(KERN_INFO |
136 | "%s: system is recognized as \"%s\"\n", | 158 | "%s: system is recognized as \"%s\"\n", |
137 | KBUILD_MODNAME, p); | 159 | KBUILD_MODNAME, name); |
138 | ret = 1; | 160 | return 1; |
139 | break; | ||
140 | } | 161 | } |
141 | } | 162 | } |
142 | 163 | ||
143 | out: | 164 | return 0; |
144 | return ret; | ||
145 | } | 165 | } |
146 | 166 | ||
147 | static struct platform_device *pdev; | 167 | static struct platform_device *pdev; |
148 | 168 | ||
149 | static int __init alix_led_init(void) | 169 | static int __init alix_pci_led_init(void) |
150 | { | 170 | { |
151 | int ret; | 171 | u32 low, hi; |
152 | 172 | ||
153 | if (!alix_present()) { | 173 | if (pci_dev_present(divil_pci) == 0) { |
154 | ret = -ENODEV; | 174 | printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); |
155 | goto out; | 175 | return -ENODEV; |
156 | } | 176 | } |
157 | 177 | ||
158 | /* enable output on GPIO for LED 1,2,3 */ | 178 | /* Grab the GPIO I/O range */ |
159 | outl(1 << 6, 0x6104); | 179 | rdmsr(MSR_LBAR_GPIO, low, hi); |
160 | outl(1 << 9, 0x6184); | 180 | |
161 | outl(1 << 11, 0x6184); | 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; | ||
162 | 215 | ||
163 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | 216 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); |
164 | if (!IS_ERR(pdev)) { | 217 | if (!IS_ERR(pdev)) { |
@@ -168,7 +221,6 @@ static int __init alix_led_init(void) | |||
168 | } else | 221 | } else |
169 | ret = PTR_ERR(pdev); | 222 | ret = PTR_ERR(pdev); |
170 | 223 | ||
171 | out: | ||
172 | return ret; | 224 | return ret; |
173 | } | 225 | } |
174 | 226 | ||
@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void) | |||
176 | { | 228 | { |
177 | platform_device_unregister(pdev); | 229 | platform_device_unregister(pdev); |
178 | platform_driver_unregister(&alix_led_driver); | 230 | platform_driver_unregister(&alix_led_driver); |
231 | release_region(gpio_base, CS5535_GPIO_SIZE); | ||
179 | } | 232 | } |
180 | 233 | ||
181 | module_init(alix_led_init); | 234 | module_init(alix_led_init); |