aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2009-10-05 08:22:27 -0400
committerRichard Purdie <rpurdie@linux.intel.com>2009-12-16 06:30:09 -0500
commit7f131cf3ed96c969d7b092bf629e25c3df50901e (patch)
tree4ea2f67bb49158eb66ef5f0e90fa9350503cb0fb
parenta328e95b82c1b8483ae5ab2f809140664d7f6c92 (diff)
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>
-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);