diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/opal-lpc.c')
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-lpc.c | 203 |
1 files changed, 203 insertions, 0 deletions
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 | } | ||