aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds/leds-alix2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/leds-alix2.c')
-rw-r--r--drivers/leds/leds-alix2.c115
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
15static int force = 0; 16static int force = 0;
16module_param(force, bool, 0444); 17module_param(force, bool, 0444);
17MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); 18MODULE_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
23static u32 gpio_base;
24
25static 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};
30MODULE_DEVICE_TABLE(pci, divil_pci);
31
19struct alix_led { 32struct 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
38static struct alix_led alix_leds[] = { 51static 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
104static int __init alix_present(void) 117static 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
143out: 164 return 0;
144 return ret;
145} 165}
146 166
147static struct platform_device *pdev; 167static struct platform_device *pdev;
148 168
149static int __init alix_led_init(void) 169static 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
203static 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
171out:
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
181module_init(alix_led_init); 234module_init(alix_led_init);