diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-07-14 23:03:11 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-08-14 00:58:08 -0400 |
commit | 3fafe9c202321a3edc47386d2071af89555c9f45 (patch) | |
tree | 7a809f8ab83049c69468e4c0a64f1ef977934d6a | |
parent | b37193b71846858d816e152d3a5db010d7b73f5e (diff) |
powerpc/powernv: Add PIO accessors for Power8 LPC bus
This uses the hooks provided by CONFIG_PPC_INDIRECT_PIO to
implement a set of hooks for IO port access to use the LPC
bus via OPAL calls for the first 64K of IO space
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/include/asm/io.h | 8 | ||||
-rw-r--r-- | arch/powerpc/include/asm/opal.h | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/io.c | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Kconfig | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-lpc.c | 203 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/powernv.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 6 |
8 files changed, 226 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h index 6cc61a3c43ee..5a64757dc0d1 100644 --- a/arch/powerpc/include/asm/io.h +++ b/arch/powerpc/include/asm/io.h | |||
@@ -69,6 +69,14 @@ extern unsigned long pci_dram_offset; | |||
69 | 69 | ||
70 | extern resource_size_t isa_mem_base; | 70 | extern resource_size_t isa_mem_base; |
71 | 71 | ||
72 | /* Boolean set by platform if PIO accesses are suppored while _IO_BASE | ||
73 | * is not set or addresses cannot be translated to MMIO. This is typically | ||
74 | * set when the platform supports "special" PIO accesses via a non memory | ||
75 | * mapped mechanism, and allows things like the early udbg UART code to | ||
76 | * function. | ||
77 | */ | ||
78 | extern bool isa_io_special; | ||
79 | |||
72 | #ifdef CONFIG_PPC32 | 80 | #ifdef CONFIG_PPC32 |
73 | #if defined(CONFIG_PPC_INDIRECT_PIO) || defined(CONFIG_PPC_INDIRECT_MMIO) | 81 | #if defined(CONFIG_PPC_INDIRECT_PIO) || defined(CONFIG_PPC_INDIRECT_MMIO) |
74 | #error CONFIG_PPC_INDIRECT_{PIO,MMIO} are not yet supported on 32 bits | 82 | #error CONFIG_PPC_INDIRECT_{PIO,MMIO} are not yet supported on 32 bits |
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index c701e2be73b1..48ad6780c6d9 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h | |||
@@ -687,6 +687,8 @@ extern int opal_machine_check(struct pt_regs *regs); | |||
687 | 687 | ||
688 | extern void opal_shutdown(void); | 688 | extern void opal_shutdown(void); |
689 | 689 | ||
690 | extern void opal_lpc_init(void); | ||
691 | |||
690 | #endif /* __ASSEMBLY__ */ | 692 | #endif /* __ASSEMBLY__ */ |
691 | 693 | ||
692 | #endif /* __OPAL_H */ | 694 | #endif /* __OPAL_H */ |
diff --git a/arch/powerpc/kernel/io.c b/arch/powerpc/kernel/io.c index 886381f32c3d..2a2b4aeab80f 100644 --- a/arch/powerpc/kernel/io.c +++ b/arch/powerpc/kernel/io.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include <asm/firmware.h> | 25 | #include <asm/firmware.h> |
26 | #include <asm/bug.h> | 26 | #include <asm/bug.h> |
27 | 27 | ||
28 | /* See definition in io.h */ | ||
29 | bool isa_io_special; | ||
30 | |||
28 | void _insb(const volatile u8 __iomem *port, void *buf, long count) | 31 | void _insb(const volatile u8 __iomem *port, void *buf, long count) |
29 | { | 32 | { |
30 | u8 *tbuf = buf; | 33 | u8 *tbuf = buf; |
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index c24684c818ab..65295870766d 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig | |||
@@ -7,6 +7,7 @@ config PPC_POWERNV | |||
7 | select PPC_P7_NAP | 7 | select PPC_P7_NAP |
8 | select PPC_PCI_CHOICE if EMBEDDED | 8 | select PPC_PCI_CHOICE if EMBEDDED |
9 | select EPAPR_BOOT | 9 | select EPAPR_BOOT |
10 | select PPC_INDIRECT_PIO | ||
10 | default y | 11 | default y |
11 | 12 | ||
12 | config POWERNV_MSI | 13 | config POWERNV_MSI |
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 7fe595152478..300c437d713c 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o | 1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o |
2 | obj-y += opal-rtc.o opal-nvram.o | 2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o |
3 | 3 | ||
4 | obj-$(CONFIG_SMP) += smp.o | 4 | obj-$(CONFIG_SMP) += smp.o |
5 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o | 5 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o |
diff --git a/arch/powerpc/platforms/powernv/opal-lpc.c b/arch/powerpc/platforms/powernv/opal-lpc.c new file mode 100644 index 000000000000..a7614bb14e17 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-lpc.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * PowerNV LPC bus handling. | ||
3 | * | ||
4 | * Copyright 2013 IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/bug.h> | ||
15 | |||
16 | #include <asm/machdep.h> | ||
17 | #include <asm/firmware.h> | ||
18 | #include <asm/xics.h> | ||
19 | #include <asm/opal.h> | ||
20 | |||
21 | static int opal_lpc_chip_id = -1; | ||
22 | |||
23 | static u8 opal_lpc_inb(unsigned long port) | ||
24 | { | ||
25 | int64_t rc; | ||
26 | uint32_t data; | ||
27 | |||
28 | if (opal_lpc_chip_id < 0 || port > 0xffff) | ||
29 | return 0xff; | ||
30 | rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 1); | ||
31 | return rc ? 0xff : data; | ||
32 | } | ||
33 | |||
34 | static __le16 __opal_lpc_inw(unsigned long port) | ||
35 | { | ||
36 | int64_t rc; | ||
37 | uint32_t data; | ||
38 | |||
39 | if (opal_lpc_chip_id < 0 || port > 0xfffe) | ||
40 | return 0xffff; | ||
41 | if (port & 1) | ||
42 | return (__le16)opal_lpc_inb(port) << 8 | opal_lpc_inb(port + 1); | ||
43 | rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 2); | ||
44 | return rc ? 0xffff : data; | ||
45 | } | ||
46 | static u16 opal_lpc_inw(unsigned long port) | ||
47 | { | ||
48 | return le16_to_cpu(__opal_lpc_inw(port)); | ||
49 | } | ||
50 | |||
51 | static __le32 __opal_lpc_inl(unsigned long port) | ||
52 | { | ||
53 | int64_t rc; | ||
54 | uint32_t data; | ||
55 | |||
56 | if (opal_lpc_chip_id < 0 || port > 0xfffc) | ||
57 | return 0xffffffff; | ||
58 | if (port & 3) | ||
59 | return (__le32)opal_lpc_inb(port ) << 24 | | ||
60 | (__le32)opal_lpc_inb(port + 1) << 16 | | ||
61 | (__le32)opal_lpc_inb(port + 2) << 8 | | ||
62 | opal_lpc_inb(port + 3); | ||
63 | rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 4); | ||
64 | return rc ? 0xffffffff : data; | ||
65 | } | ||
66 | |||
67 | static u32 opal_lpc_inl(unsigned long port) | ||
68 | { | ||
69 | return le32_to_cpu(__opal_lpc_inl(port)); | ||
70 | } | ||
71 | |||
72 | static void opal_lpc_outb(u8 val, unsigned long port) | ||
73 | { | ||
74 | if (opal_lpc_chip_id < 0 || port > 0xffff) | ||
75 | return; | ||
76 | opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 1); | ||
77 | } | ||
78 | |||
79 | static void __opal_lpc_outw(__le16 val, unsigned long port) | ||
80 | { | ||
81 | if (opal_lpc_chip_id < 0 || port > 0xfffe) | ||
82 | return; | ||
83 | if (port & 1) { | ||
84 | opal_lpc_outb(val >> 8, port); | ||
85 | opal_lpc_outb(val , port + 1); | ||
86 | return; | ||
87 | } | ||
88 | opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 2); | ||
89 | } | ||
90 | |||
91 | static void opal_lpc_outw(u16 val, unsigned long port) | ||
92 | { | ||
93 | __opal_lpc_outw(cpu_to_le16(val), port); | ||
94 | } | ||
95 | |||
96 | static void __opal_lpc_outl(__le32 val, unsigned long port) | ||
97 | { | ||
98 | if (opal_lpc_chip_id < 0 || port > 0xfffc) | ||
99 | return; | ||
100 | if (port & 3) { | ||
101 | opal_lpc_outb(val >> 24, port); | ||
102 | opal_lpc_outb(val >> 16, port + 1); | ||
103 | opal_lpc_outb(val >> 8, port + 2); | ||
104 | opal_lpc_outb(val , port + 3); | ||
105 | return; | ||
106 | } | ||
107 | opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 4); | ||
108 | } | ||
109 | |||
110 | static void opal_lpc_outl(u32 val, unsigned long port) | ||
111 | { | ||
112 | __opal_lpc_outl(cpu_to_le32(val), port); | ||
113 | } | ||
114 | |||
115 | static void opal_lpc_insb(unsigned long p, void *b, unsigned long c) | ||
116 | { | ||
117 | u8 *ptr = b; | ||
118 | |||
119 | while(c--) | ||
120 | *(ptr++) = opal_lpc_inb(p); | ||
121 | } | ||
122 | |||
123 | static void opal_lpc_insw(unsigned long p, void *b, unsigned long c) | ||
124 | { | ||
125 | __le16 *ptr = b; | ||
126 | |||
127 | while(c--) | ||
128 | *(ptr++) = __opal_lpc_inw(p); | ||
129 | } | ||
130 | |||
131 | static void opal_lpc_insl(unsigned long p, void *b, unsigned long c) | ||
132 | { | ||
133 | __le32 *ptr = b; | ||
134 | |||
135 | while(c--) | ||
136 | *(ptr++) = __opal_lpc_inl(p); | ||
137 | } | ||
138 | |||
139 | static void opal_lpc_outsb(unsigned long p, const void *b, unsigned long c) | ||
140 | { | ||
141 | const u8 *ptr = b; | ||
142 | |||
143 | while(c--) | ||
144 | opal_lpc_outb(*(ptr++), p); | ||
145 | } | ||
146 | |||
147 | static void opal_lpc_outsw(unsigned long p, const void *b, unsigned long c) | ||
148 | { | ||
149 | const __le16 *ptr = b; | ||
150 | |||
151 | while(c--) | ||
152 | __opal_lpc_outw(*(ptr++), p); | ||
153 | } | ||
154 | |||
155 | static void opal_lpc_outsl(unsigned long p, const void *b, unsigned long c) | ||
156 | { | ||
157 | const __le32 *ptr = b; | ||
158 | |||
159 | while(c--) | ||
160 | __opal_lpc_outl(*(ptr++), p); | ||
161 | } | ||
162 | |||
163 | static const struct ppc_pci_io opal_lpc_io = { | ||
164 | .inb = opal_lpc_inb, | ||
165 | .inw = opal_lpc_inw, | ||
166 | .inl = opal_lpc_inl, | ||
167 | .outb = opal_lpc_outb, | ||
168 | .outw = opal_lpc_outw, | ||
169 | .outl = opal_lpc_outl, | ||
170 | .insb = opal_lpc_insb, | ||
171 | .insw = opal_lpc_insw, | ||
172 | .insl = opal_lpc_insl, | ||
173 | .outsb = opal_lpc_outsb, | ||
174 | .outsw = opal_lpc_outsw, | ||
175 | .outsl = opal_lpc_outsl, | ||
176 | }; | ||
177 | |||
178 | void opal_lpc_init(void) | ||
179 | { | ||
180 | struct device_node *np; | ||
181 | |||
182 | /* | ||
183 | * Look for a Power8 LPC bus tagged as "primary", | ||
184 | * we currently support only one though the OPAL APIs | ||
185 | * support any number. | ||
186 | */ | ||
187 | for_each_compatible_node(np, NULL, "ibm,power8-lpc") { | ||
188 | if (!of_device_is_available(np)) | ||
189 | continue; | ||
190 | if (!of_get_property(np, "primary", NULL)) | ||
191 | continue; | ||
192 | opal_lpc_chip_id = of_get_ibm_chip_id(np); | ||
193 | break; | ||
194 | } | ||
195 | if (opal_lpc_chip_id < 0) | ||
196 | return; | ||
197 | |||
198 | /* Setup special IO ops */ | ||
199 | ppc_pci_io = opal_lpc_io; | ||
200 | isa_io_special = true; | ||
201 | |||
202 | pr_info("OPAL: Power8 LPC bus found, chip ID %d\n", opal_lpc_chip_id); | ||
203 | } | ||
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h index a1c6f83fc391..de6819be1f95 100644 --- a/arch/powerpc/platforms/powernv/powernv.h +++ b/arch/powerpc/platforms/powernv/powernv.h | |||
@@ -15,4 +15,6 @@ static inline void pnv_pci_init(void) { } | |||
15 | static inline void pnv_pci_shutdown(void) { } | 15 | static inline void pnv_pci_shutdown(void) { } |
16 | #endif | 16 | #endif |
17 | 17 | ||
18 | extern void pnv_lpc_init(void); | ||
19 | |||
18 | #endif /* _POWERNV_H */ | 20 | #endif /* _POWERNV_H */ |
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 84438af96c05..4ddb339700b9 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c | |||
@@ -54,6 +54,12 @@ static void __init pnv_setup_arch(void) | |||
54 | 54 | ||
55 | static void __init pnv_init_early(void) | 55 | static void __init pnv_init_early(void) |
56 | { | 56 | { |
57 | /* | ||
58 | * Initialize the LPC bus now so that legacy serial | ||
59 | * ports can be found on it | ||
60 | */ | ||
61 | opal_lpc_init(); | ||
62 | |||
57 | #ifdef CONFIG_HVC_OPAL | 63 | #ifdef CONFIG_HVC_OPAL |
58 | if (firmware_has_feature(FW_FEATURE_OPAL)) | 64 | if (firmware_has_feature(FW_FEATURE_OPAL)) |
59 | hvc_opal_init_early(); | 65 | hvc_opal_init_early(); |