diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/Kconfig | 21 | ||||
-rw-r--r-- | arch/x86/include/asm/amd_nb.h | 17 | ||||
-rw-r--r-- | arch/x86/include/asm/processor.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/apb_timer.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/amd.c | 4 | ||||
-rw-r--r-- | arch/x86/mm/srat.c | 29 | ||||
-rw-r--r-- | arch/x86/platform/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/platform/goldfish/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/goldfish/goldfish.c | 51 | ||||
-rw-r--r-- | arch/x86/platform/ts5500/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/ts5500/ts5500.c | 339 |
11 files changed, 451 insertions, 18 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5e05ee3a9810..260857a53b87 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -323,6 +323,10 @@ config X86_BIGSMP | |||
323 | ---help--- | 323 | ---help--- |
324 | This option is needed for the systems that have more than 8 CPUs | 324 | This option is needed for the systems that have more than 8 CPUs |
325 | 325 | ||
326 | config GOLDFISH | ||
327 | def_bool y | ||
328 | depends on X86_GOLDFISH | ||
329 | |||
326 | if X86_32 | 330 | if X86_32 |
327 | config X86_EXTENDED_PLATFORM | 331 | config X86_EXTENDED_PLATFORM |
328 | bool "Support for extended (non-PC) x86 platforms" | 332 | bool "Support for extended (non-PC) x86 platforms" |
@@ -405,6 +409,14 @@ config X86_UV | |||
405 | # Following is an alphabetically sorted list of 32 bit extended platforms | 409 | # Following is an alphabetically sorted list of 32 bit extended platforms |
406 | # Please maintain the alphabetic order if and when there are additions | 410 | # Please maintain the alphabetic order if and when there are additions |
407 | 411 | ||
412 | config X86_GOLDFISH | ||
413 | bool "Goldfish (Virtual Platform)" | ||
414 | depends on X86_32 | ||
415 | ---help--- | ||
416 | Enable support for the Goldfish virtual platform used primarily | ||
417 | for Android development. Unless you are building for the Android | ||
418 | Goldfish emulator say N here. | ||
419 | |||
408 | config X86_INTEL_CE | 420 | config X86_INTEL_CE |
409 | bool "CE4100 TV platform" | 421 | bool "CE4100 TV platform" |
410 | depends on PCI | 422 | depends on PCI |
@@ -2191,6 +2203,15 @@ config GEOS | |||
2191 | ---help--- | 2203 | ---help--- |
2192 | This option enables system support for the Traverse Technologies GEOS. | 2204 | This option enables system support for the Traverse Technologies GEOS. |
2193 | 2205 | ||
2206 | config TS5500 | ||
2207 | bool "Technologic Systems TS-5500 platform support" | ||
2208 | depends on MELAN | ||
2209 | select CHECK_SIGNATURE | ||
2210 | select NEW_LEDS | ||
2211 | select LEDS_CLASS | ||
2212 | ---help--- | ||
2213 | This option enables system support for the Technologic Systems TS-5500. | ||
2214 | |||
2194 | endif # X86_32 | 2215 | endif # X86_32 |
2195 | 2216 | ||
2196 | config AMD_NB | 2217 | config AMD_NB |
diff --git a/arch/x86/include/asm/amd_nb.h b/arch/x86/include/asm/amd_nb.h index b3341e9cd8fd..a54ee1d054d9 100644 --- a/arch/x86/include/asm/amd_nb.h +++ b/arch/x86/include/asm/amd_nb.h | |||
@@ -81,6 +81,23 @@ static inline struct amd_northbridge *node_to_amd_nb(int node) | |||
81 | return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL; | 81 | return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL; |
82 | } | 82 | } |
83 | 83 | ||
84 | static inline u16 amd_get_node_id(struct pci_dev *pdev) | ||
85 | { | ||
86 | struct pci_dev *misc; | ||
87 | int i; | ||
88 | |||
89 | for (i = 0; i != amd_nb_num(); i++) { | ||
90 | misc = node_to_amd_nb(i)->misc; | ||
91 | |||
92 | if (pci_domain_nr(misc->bus) == pci_domain_nr(pdev->bus) && | ||
93 | PCI_SLOT(misc->devfn) == PCI_SLOT(pdev->devfn)) | ||
94 | return i; | ||
95 | } | ||
96 | |||
97 | WARN(1, "Unable to find AMD Northbridge id for %s\n", pci_name(pdev)); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
84 | #else | 101 | #else |
85 | 102 | ||
86 | #define amd_nb_num(x) 0 | 103 | #define amd_nb_num(x) 0 |
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 888184b2fc85..cf500543f6ff 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -943,7 +943,7 @@ extern void start_thread(struct pt_regs *regs, unsigned long new_ip, | |||
943 | extern int get_tsc_mode(unsigned long adr); | 943 | extern int get_tsc_mode(unsigned long adr); |
944 | extern int set_tsc_mode(unsigned int val); | 944 | extern int set_tsc_mode(unsigned int val); |
945 | 945 | ||
946 | extern int amd_get_nb_id(int cpu); | 946 | extern u16 amd_get_nb_id(int cpu); |
947 | 947 | ||
948 | struct aperfmperf { | 948 | struct aperfmperf { |
949 | u64 aperf, mperf; | 949 | u64 aperf, mperf; |
diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index cc74fd0c90f2..c9876efecafb 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c | |||
@@ -240,7 +240,7 @@ static int apbt_cpuhp_notify(struct notifier_block *n, | |||
240 | dw_apb_clockevent_pause(adev->timer); | 240 | dw_apb_clockevent_pause(adev->timer); |
241 | if (system_state == SYSTEM_RUNNING) { | 241 | if (system_state == SYSTEM_RUNNING) { |
242 | pr_debug("skipping APBT CPU %lu offline\n", cpu); | 242 | pr_debug("skipping APBT CPU %lu offline\n", cpu); |
243 | } else if (adev) { | 243 | } else { |
244 | pr_debug("APBT clockevent for cpu %lu offline\n", cpu); | 244 | pr_debug("APBT clockevent for cpu %lu offline\n", cpu); |
245 | dw_apb_clockevent_stop(adev->timer); | 245 | dw_apb_clockevent_stop(adev->timer); |
246 | } | 246 | } |
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 15239fffd6fe..782c456eaa01 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c | |||
@@ -364,9 +364,9 @@ static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c) | |||
364 | #endif | 364 | #endif |
365 | } | 365 | } |
366 | 366 | ||
367 | int amd_get_nb_id(int cpu) | 367 | u16 amd_get_nb_id(int cpu) |
368 | { | 368 | { |
369 | int id = 0; | 369 | u16 id = 0; |
370 | #ifdef CONFIG_SMP | 370 | #ifdef CONFIG_SMP |
371 | id = per_cpu(cpu_llc_id, cpu); | 371 | id = per_cpu(cpu_llc_id, cpu); |
372 | #endif | 372 | #endif |
diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 4ddf497ca65b..cdd0da9dd530 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c | |||
@@ -149,39 +149,40 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) | |||
149 | int node, pxm; | 149 | int node, pxm; |
150 | 150 | ||
151 | if (srat_disabled()) | 151 | if (srat_disabled()) |
152 | return -1; | 152 | goto out_err; |
153 | if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) { | 153 | if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) |
154 | bad_srat(); | 154 | goto out_err_bad_srat; |
155 | return -1; | ||
156 | } | ||
157 | if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) | 155 | if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) |
158 | return -1; | 156 | goto out_err; |
159 | |||
160 | if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) | 157 | if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) |
161 | return -1; | 158 | goto out_err; |
159 | |||
162 | start = ma->base_address; | 160 | start = ma->base_address; |
163 | end = start + ma->length; | 161 | end = start + ma->length; |
164 | pxm = ma->proximity_domain; | 162 | pxm = ma->proximity_domain; |
165 | if (acpi_srat_revision <= 1) | 163 | if (acpi_srat_revision <= 1) |
166 | pxm &= 0xff; | 164 | pxm &= 0xff; |
165 | |||
167 | node = setup_node(pxm); | 166 | node = setup_node(pxm); |
168 | if (node < 0) { | 167 | if (node < 0) { |
169 | printk(KERN_ERR "SRAT: Too many proximity domains.\n"); | 168 | printk(KERN_ERR "SRAT: Too many proximity domains.\n"); |
170 | bad_srat(); | 169 | goto out_err_bad_srat; |
171 | return -1; | ||
172 | } | 170 | } |
173 | 171 | ||
174 | if (numa_add_memblk(node, start, end) < 0) { | 172 | if (numa_add_memblk(node, start, end) < 0) |
175 | bad_srat(); | 173 | goto out_err_bad_srat; |
176 | return -1; | ||
177 | } | ||
178 | 174 | ||
179 | node_set(node, numa_nodes_parsed); | 175 | node_set(node, numa_nodes_parsed); |
180 | 176 | ||
181 | printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n", | 177 | printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n", |
182 | node, pxm, | 178 | node, pxm, |
183 | (unsigned long long) start, (unsigned long long) end - 1); | 179 | (unsigned long long) start, (unsigned long long) end - 1); |
180 | |||
184 | return 0; | 181 | return 0; |
182 | out_err_bad_srat: | ||
183 | bad_srat(); | ||
184 | out_err: | ||
185 | return -1; | ||
185 | } | 186 | } |
186 | 187 | ||
187 | void __init acpi_numa_arch_fixup(void) {} | 188 | void __init acpi_numa_arch_fixup(void) {} |
diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index 8d874396cb29..01e0231a113e 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile | |||
@@ -2,10 +2,12 @@ | |||
2 | obj-y += ce4100/ | 2 | obj-y += ce4100/ |
3 | obj-y += efi/ | 3 | obj-y += efi/ |
4 | obj-y += geode/ | 4 | obj-y += geode/ |
5 | obj-y += goldfish/ | ||
5 | obj-y += iris/ | 6 | obj-y += iris/ |
6 | obj-y += mrst/ | 7 | obj-y += mrst/ |
7 | obj-y += olpc/ | 8 | obj-y += olpc/ |
8 | obj-y += scx200/ | 9 | obj-y += scx200/ |
9 | obj-y += sfi/ | 10 | obj-y += sfi/ |
11 | obj-y += ts5500/ | ||
10 | obj-y += visws/ | 12 | obj-y += visws/ |
11 | obj-y += uv/ | 13 | obj-y += uv/ |
diff --git a/arch/x86/platform/goldfish/Makefile b/arch/x86/platform/goldfish/Makefile new file mode 100644 index 000000000000..f030b532fdf3 --- /dev/null +++ b/arch/x86/platform/goldfish/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_GOLDFISH) += goldfish.o | |||
diff --git a/arch/x86/platform/goldfish/goldfish.c b/arch/x86/platform/goldfish/goldfish.c new file mode 100644 index 000000000000..1693107a518e --- /dev/null +++ b/arch/x86/platform/goldfish/goldfish.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Google, Inc. | ||
3 | * Copyright (C) 2011 Intel, Inc. | ||
4 | * Copyright (C) 2013 Intel, Inc. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | /* | ||
22 | * Where in virtual device memory the IO devices (timers, system controllers | ||
23 | * and so on) | ||
24 | */ | ||
25 | |||
26 | #define GOLDFISH_PDEV_BUS_BASE (0xff001000) | ||
27 | #define GOLDFISH_PDEV_BUS_END (0xff7fffff) | ||
28 | #define GOLDFISH_PDEV_BUS_IRQ (4) | ||
29 | |||
30 | #define GOLDFISH_TTY_BASE (0x2000) | ||
31 | |||
32 | static struct resource goldfish_pdev_bus_resources[] = { | ||
33 | { | ||
34 | .start = GOLDFISH_PDEV_BUS_BASE, | ||
35 | .end = GOLDFISH_PDEV_BUS_END, | ||
36 | .flags = IORESOURCE_MEM, | ||
37 | }, | ||
38 | { | ||
39 | .start = GOLDFISH_PDEV_BUS_IRQ, | ||
40 | .end = GOLDFISH_PDEV_BUS_IRQ, | ||
41 | .flags = IORESOURCE_IRQ, | ||
42 | } | ||
43 | }; | ||
44 | |||
45 | static int __init goldfish_init(void) | ||
46 | { | ||
47 | platform_device_register_simple("goldfish_pdev_bus", -1, | ||
48 | goldfish_pdev_bus_resources, 2); | ||
49 | return 0; | ||
50 | } | ||
51 | device_initcall(goldfish_init); | ||
diff --git a/arch/x86/platform/ts5500/Makefile b/arch/x86/platform/ts5500/Makefile new file mode 100644 index 000000000000..c54e348c96a7 --- /dev/null +++ b/arch/x86/platform/ts5500/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_TS5500) += ts5500.o | |||
diff --git a/arch/x86/platform/ts5500/ts5500.c b/arch/x86/platform/ts5500/ts5500.c new file mode 100644 index 000000000000..39febb214e8c --- /dev/null +++ b/arch/x86/platform/ts5500/ts5500.c | |||
@@ -0,0 +1,339 @@ | |||
1 | /* | ||
2 | * Technologic Systems TS-5500 Single Board Computer support | ||
3 | * | ||
4 | * Copyright (C) 2013 Savoir-faire Linux Inc. | ||
5 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it under | ||
8 | * the terms of the GNU General Public License as published by the Free Software | ||
9 | * Foundation; either version 2 of the License, or (at your option) any later | ||
10 | * version. | ||
11 | * | ||
12 | * | ||
13 | * This driver registers the Technologic Systems TS-5500 Single Board Computer | ||
14 | * (SBC) and its devices, and exposes information to userspace such as jumpers' | ||
15 | * state or available options. For further information about sysfs entries, see | ||
16 | * Documentation/ABI/testing/sysfs-platform-ts5500. | ||
17 | * | ||
18 | * This code actually supports the TS-5500 platform, but it may be extended to | ||
19 | * support similar Technologic Systems x86-based platforms, such as the TS-5600. | ||
20 | */ | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/leds.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/platform_data/gpio-ts5500.h> | ||
28 | #include <linux/platform_data/max197.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/slab.h> | ||
31 | |||
32 | /* Product code register */ | ||
33 | #define TS5500_PRODUCT_CODE_ADDR 0x74 | ||
34 | #define TS5500_PRODUCT_CODE 0x60 /* TS-5500 product code */ | ||
35 | |||
36 | /* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */ | ||
37 | #define TS5500_SRAM_RS485_ADC_ADDR 0x75 | ||
38 | #define TS5500_SRAM BIT(0) /* SRAM option */ | ||
39 | #define TS5500_RS485 BIT(1) /* RS-485 option */ | ||
40 | #define TS5500_ADC BIT(2) /* A/D converter option */ | ||
41 | #define TS5500_RS485_RTS BIT(6) /* RTS for RS-485 */ | ||
42 | #define TS5500_RS485_AUTO BIT(7) /* Automatic RS-485 */ | ||
43 | |||
44 | /* External Reset/Industrial Temperature Range options register */ | ||
45 | #define TS5500_ERESET_ITR_ADDR 0x76 | ||
46 | #define TS5500_ERESET BIT(0) /* External Reset option */ | ||
47 | #define TS5500_ITR BIT(1) /* Indust. Temp. Range option */ | ||
48 | |||
49 | /* LED/Jumpers register */ | ||
50 | #define TS5500_LED_JP_ADDR 0x77 | ||
51 | #define TS5500_LED BIT(0) /* LED flag */ | ||
52 | #define TS5500_JP1 BIT(1) /* Automatic CMOS */ | ||
53 | #define TS5500_JP2 BIT(2) /* Enable Serial Console */ | ||
54 | #define TS5500_JP3 BIT(3) /* Write Enable Drive A */ | ||
55 | #define TS5500_JP4 BIT(4) /* Fast Console (115K baud) */ | ||
56 | #define TS5500_JP5 BIT(5) /* User Jumper */ | ||
57 | #define TS5500_JP6 BIT(6) /* Console on COM1 (req. JP2) */ | ||
58 | #define TS5500_JP7 BIT(7) /* Undocumented (Unused) */ | ||
59 | |||
60 | /* A/D Converter registers */ | ||
61 | #define TS5500_ADC_CONV_BUSY_ADDR 0x195 /* Conversion state register */ | ||
62 | #define TS5500_ADC_CONV_BUSY BIT(0) | ||
63 | #define TS5500_ADC_CONV_INIT_LSB_ADDR 0x196 /* Start conv. / LSB register */ | ||
64 | #define TS5500_ADC_CONV_MSB_ADDR 0x197 /* MSB register */ | ||
65 | #define TS5500_ADC_CONV_DELAY 12 /* usec */ | ||
66 | |||
67 | /** | ||
68 | * struct ts5500_sbc - TS-5500 board description | ||
69 | * @id: Board product ID. | ||
70 | * @sram: Flag for SRAM option. | ||
71 | * @rs485: Flag for RS-485 option. | ||
72 | * @adc: Flag for Analog/Digital converter option. | ||
73 | * @ereset: Flag for External Reset option. | ||
74 | * @itr: Flag for Industrial Temperature Range option. | ||
75 | * @jumpers: Bitfield for jumpers' state. | ||
76 | */ | ||
77 | struct ts5500_sbc { | ||
78 | int id; | ||
79 | bool sram; | ||
80 | bool rs485; | ||
81 | bool adc; | ||
82 | bool ereset; | ||
83 | bool itr; | ||
84 | u8 jumpers; | ||
85 | }; | ||
86 | |||
87 | /* Board signatures in BIOS shadow RAM */ | ||
88 | static const struct { | ||
89 | const char * const string; | ||
90 | const ssize_t offset; | ||
91 | } ts5500_signatures[] __initdata = { | ||
92 | { "TS-5x00 AMD Elan", 0xb14 }, | ||
93 | }; | ||
94 | |||
95 | static int __init ts5500_check_signature(void) | ||
96 | { | ||
97 | void __iomem *bios; | ||
98 | int i, ret = -ENODEV; | ||
99 | |||
100 | bios = ioremap(0xf0000, 0x10000); | ||
101 | if (!bios) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) { | ||
105 | if (check_signature(bios + ts5500_signatures[i].offset, | ||
106 | ts5500_signatures[i].string, | ||
107 | strlen(ts5500_signatures[i].string))) { | ||
108 | ret = 0; | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | iounmap(bios); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static int __init ts5500_detect_config(struct ts5500_sbc *sbc) | ||
118 | { | ||
119 | u8 tmp; | ||
120 | int ret = 0; | ||
121 | |||
122 | if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500")) | ||
123 | return -EBUSY; | ||
124 | |||
125 | tmp = inb(TS5500_PRODUCT_CODE_ADDR); | ||
126 | if (tmp != TS5500_PRODUCT_CODE) { | ||
127 | pr_err("This platform is not a TS-5500 (found ID 0x%x)\n", tmp); | ||
128 | ret = -ENODEV; | ||
129 | goto cleanup; | ||
130 | } | ||
131 | sbc->id = tmp; | ||
132 | |||
133 | tmp = inb(TS5500_SRAM_RS485_ADC_ADDR); | ||
134 | sbc->sram = tmp & TS5500_SRAM; | ||
135 | sbc->rs485 = tmp & TS5500_RS485; | ||
136 | sbc->adc = tmp & TS5500_ADC; | ||
137 | |||
138 | tmp = inb(TS5500_ERESET_ITR_ADDR); | ||
139 | sbc->ereset = tmp & TS5500_ERESET; | ||
140 | sbc->itr = tmp & TS5500_ITR; | ||
141 | |||
142 | tmp = inb(TS5500_LED_JP_ADDR); | ||
143 | sbc->jumpers = tmp & ~TS5500_LED; | ||
144 | |||
145 | cleanup: | ||
146 | release_region(TS5500_PRODUCT_CODE_ADDR, 4); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static ssize_t ts5500_show_id(struct device *dev, | ||
151 | struct device_attribute *attr, char *buf) | ||
152 | { | ||
153 | struct ts5500_sbc *sbc = dev_get_drvdata(dev); | ||
154 | |||
155 | return sprintf(buf, "0x%.2x\n", sbc->id); | ||
156 | } | ||
157 | |||
158 | static ssize_t ts5500_show_jumpers(struct device *dev, | ||
159 | struct device_attribute *attr, | ||
160 | char *buf) | ||
161 | { | ||
162 | struct ts5500_sbc *sbc = dev_get_drvdata(dev); | ||
163 | |||
164 | return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1); | ||
165 | } | ||
166 | |||
167 | #define TS5500_SHOW(field) \ | ||
168 | static ssize_t ts5500_show_##field(struct device *dev, \ | ||
169 | struct device_attribute *attr, \ | ||
170 | char *buf) \ | ||
171 | { \ | ||
172 | struct ts5500_sbc *sbc = dev_get_drvdata(dev); \ | ||
173 | return sprintf(buf, "%d\n", sbc->field); \ | ||
174 | } | ||
175 | |||
176 | TS5500_SHOW(sram) | ||
177 | TS5500_SHOW(rs485) | ||
178 | TS5500_SHOW(adc) | ||
179 | TS5500_SHOW(ereset) | ||
180 | TS5500_SHOW(itr) | ||
181 | |||
182 | static DEVICE_ATTR(id, S_IRUGO, ts5500_show_id, NULL); | ||
183 | static DEVICE_ATTR(jumpers, S_IRUGO, ts5500_show_jumpers, NULL); | ||
184 | static DEVICE_ATTR(sram, S_IRUGO, ts5500_show_sram, NULL); | ||
185 | static DEVICE_ATTR(rs485, S_IRUGO, ts5500_show_rs485, NULL); | ||
186 | static DEVICE_ATTR(adc, S_IRUGO, ts5500_show_adc, NULL); | ||
187 | static DEVICE_ATTR(ereset, S_IRUGO, ts5500_show_ereset, NULL); | ||
188 | static DEVICE_ATTR(itr, S_IRUGO, ts5500_show_itr, NULL); | ||
189 | |||
190 | static struct attribute *ts5500_attributes[] = { | ||
191 | &dev_attr_id.attr, | ||
192 | &dev_attr_jumpers.attr, | ||
193 | &dev_attr_sram.attr, | ||
194 | &dev_attr_rs485.attr, | ||
195 | &dev_attr_adc.attr, | ||
196 | &dev_attr_ereset.attr, | ||
197 | &dev_attr_itr.attr, | ||
198 | NULL | ||
199 | }; | ||
200 | |||
201 | static const struct attribute_group ts5500_attr_group = { | ||
202 | .attrs = ts5500_attributes, | ||
203 | }; | ||
204 | |||
205 | static struct resource ts5500_dio1_resource[] = { | ||
206 | DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"), | ||
207 | }; | ||
208 | |||
209 | static struct platform_device ts5500_dio1_pdev = { | ||
210 | .name = "ts5500-dio1", | ||
211 | .id = -1, | ||
212 | .resource = ts5500_dio1_resource, | ||
213 | .num_resources = 1, | ||
214 | }; | ||
215 | |||
216 | static struct resource ts5500_dio2_resource[] = { | ||
217 | DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"), | ||
218 | }; | ||
219 | |||
220 | static struct platform_device ts5500_dio2_pdev = { | ||
221 | .name = "ts5500-dio2", | ||
222 | .id = -1, | ||
223 | .resource = ts5500_dio2_resource, | ||
224 | .num_resources = 1, | ||
225 | }; | ||
226 | |||
227 | static void ts5500_led_set(struct led_classdev *led_cdev, | ||
228 | enum led_brightness brightness) | ||
229 | { | ||
230 | outb(!!brightness, TS5500_LED_JP_ADDR); | ||
231 | } | ||
232 | |||
233 | static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev) | ||
234 | { | ||
235 | return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF; | ||
236 | } | ||
237 | |||
238 | static struct led_classdev ts5500_led_cdev = { | ||
239 | .name = "ts5500:green:", | ||
240 | .brightness_set = ts5500_led_set, | ||
241 | .brightness_get = ts5500_led_get, | ||
242 | }; | ||
243 | |||
244 | static int ts5500_adc_convert(u8 ctrl) | ||
245 | { | ||
246 | u8 lsb, msb; | ||
247 | |||
248 | /* Start conversion (ensure the 3 MSB are set to 0) */ | ||
249 | outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR); | ||
250 | |||
251 | /* | ||
252 | * The platform has CPLD logic driving the A/D converter. | ||
253 | * The conversion must complete within 11 microseconds, | ||
254 | * otherwise we have to re-initiate a conversion. | ||
255 | */ | ||
256 | udelay(TS5500_ADC_CONV_DELAY); | ||
257 | if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY) | ||
258 | return -EBUSY; | ||
259 | |||
260 | /* Read the raw data */ | ||
261 | lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR); | ||
262 | msb = inb(TS5500_ADC_CONV_MSB_ADDR); | ||
263 | |||
264 | return (msb << 8) | lsb; | ||
265 | } | ||
266 | |||
267 | static struct max197_platform_data ts5500_adc_pdata = { | ||
268 | .convert = ts5500_adc_convert, | ||
269 | }; | ||
270 | |||
271 | static struct platform_device ts5500_adc_pdev = { | ||
272 | .name = "max197", | ||
273 | .id = -1, | ||
274 | .dev = { | ||
275 | .platform_data = &ts5500_adc_pdata, | ||
276 | }, | ||
277 | }; | ||
278 | |||
279 | static int __init ts5500_init(void) | ||
280 | { | ||
281 | struct platform_device *pdev; | ||
282 | struct ts5500_sbc *sbc; | ||
283 | int err; | ||
284 | |||
285 | /* | ||
286 | * There is no DMI available or PCI bridge subvendor info, | ||
287 | * only the BIOS provides a 16-bit identification call. | ||
288 | * It is safer to find a signature in the BIOS shadow RAM. | ||
289 | */ | ||
290 | err = ts5500_check_signature(); | ||
291 | if (err) | ||
292 | return err; | ||
293 | |||
294 | pdev = platform_device_register_simple("ts5500", -1, NULL, 0); | ||
295 | if (IS_ERR(pdev)) | ||
296 | return PTR_ERR(pdev); | ||
297 | |||
298 | sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL); | ||
299 | if (!sbc) { | ||
300 | err = -ENOMEM; | ||
301 | goto error; | ||
302 | } | ||
303 | |||
304 | err = ts5500_detect_config(sbc); | ||
305 | if (err) | ||
306 | goto error; | ||
307 | |||
308 | platform_set_drvdata(pdev, sbc); | ||
309 | |||
310 | err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group); | ||
311 | if (err) | ||
312 | goto error; | ||
313 | |||
314 | ts5500_dio1_pdev.dev.parent = &pdev->dev; | ||
315 | if (platform_device_register(&ts5500_dio1_pdev)) | ||
316 | dev_warn(&pdev->dev, "DIO1 block registration failed\n"); | ||
317 | ts5500_dio2_pdev.dev.parent = &pdev->dev; | ||
318 | if (platform_device_register(&ts5500_dio2_pdev)) | ||
319 | dev_warn(&pdev->dev, "DIO2 block registration failed\n"); | ||
320 | |||
321 | if (led_classdev_register(&pdev->dev, &ts5500_led_cdev)) | ||
322 | dev_warn(&pdev->dev, "LED registration failed\n"); | ||
323 | |||
324 | if (sbc->adc) { | ||
325 | ts5500_adc_pdev.dev.parent = &pdev->dev; | ||
326 | if (platform_device_register(&ts5500_adc_pdev)) | ||
327 | dev_warn(&pdev->dev, "ADC registration failed\n"); | ||
328 | } | ||
329 | |||
330 | return 0; | ||
331 | error: | ||
332 | platform_device_unregister(pdev); | ||
333 | return err; | ||
334 | } | ||
335 | device_initcall(ts5500_init); | ||
336 | |||
337 | MODULE_LICENSE("GPL"); | ||
338 | MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); | ||
339 | MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver"); | ||