Commit | Line | Data |
---|---|---|
243ac210 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
9f88145f CM |
2 | /* |
3 | * A hack to create a platform device from a DMI entry. This will | |
4 | * allow autoloading of the IPMI drive based on SMBIOS entries. | |
5 | */ | |
6 | ||
9abcfaaa CM |
7 | #define pr_fmt(fmt) "%s" fmt, "ipmi:dmi: " |
8 | #define dev_fmt pr_fmt | |
9 | ||
9f88145f CM |
10 | #include <linux/ipmi.h> |
11 | #include <linux/init.h> | |
12 | #include <linux/dmi.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/property.h> | |
95e300c0 | 15 | #include "ipmi_si_sm.h" |
9f88145f CM |
16 | #include "ipmi_dmi.h" |
17 | ||
95e300c0 CM |
18 | #define IPMI_DMI_TYPE_KCS 0x01 |
19 | #define IPMI_DMI_TYPE_SMIC 0x02 | |
20 | #define IPMI_DMI_TYPE_BT 0x03 | |
21 | #define IPMI_DMI_TYPE_SSIF 0x04 | |
22 | ||
9f88145f | 23 | struct ipmi_dmi_info { |
95e300c0 | 24 | enum si_type si_type; |
9f88145f CM |
25 | u32 flags; |
26 | unsigned long addr; | |
27 | u8 slave_addr; | |
28 | struct ipmi_dmi_info *next; | |
29 | }; | |
30 | ||
31 | static struct ipmi_dmi_info *ipmi_dmi_infos; | |
32 | ||
33 | static int ipmi_dmi_nr __initdata; | |
34 | ||
35 | static void __init dmi_add_platform_ipmi(unsigned long base_addr, | |
36 | u32 flags, | |
37 | u8 slave_addr, | |
38 | int irq, | |
39 | int offset, | |
40 | int type) | |
41 | { | |
42 | struct platform_device *pdev; | |
43 | struct resource r[4]; | |
44 | unsigned int num_r = 1, size; | |
95e300c0 CM |
45 | struct property_entry p[5]; |
46 | unsigned int pidx = 0; | |
9f88145f CM |
47 | char *name, *override; |
48 | int rv; | |
95e300c0 | 49 | enum si_type si_type; |
9f88145f CM |
50 | struct ipmi_dmi_info *info; |
51 | ||
95e300c0 | 52 | memset(p, 0, sizeof(p)); |
9f88145f CM |
53 | |
54 | name = "dmi-ipmi-si"; | |
55 | override = "ipmi_si"; | |
56 | switch (type) { | |
57 | case IPMI_DMI_TYPE_SSIF: | |
58 | name = "dmi-ipmi-ssif"; | |
59 | override = "ipmi_ssif"; | |
60 | offset = 1; | |
61 | size = 1; | |
95e300c0 | 62 | si_type = SI_TYPE_INVALID; |
9f88145f CM |
63 | break; |
64 | case IPMI_DMI_TYPE_BT: | |
65 | size = 3; | |
95e300c0 | 66 | si_type = SI_BT; |
9f88145f CM |
67 | break; |
68 | case IPMI_DMI_TYPE_KCS: | |
95e300c0 CM |
69 | size = 2; |
70 | si_type = SI_KCS; | |
71 | break; | |
9f88145f CM |
72 | case IPMI_DMI_TYPE_SMIC: |
73 | size = 2; | |
95e300c0 | 74 | si_type = SI_SMIC; |
9f88145f CM |
75 | break; |
76 | default: | |
9abcfaaa | 77 | pr_err("Invalid IPMI type: %d\n", type); |
9f88145f CM |
78 | return; |
79 | } | |
80 | ||
95e300c0 | 81 | if (si_type != SI_TYPE_INVALID) |
c5b24091 AS |
82 | p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type); |
83 | ||
84 | p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr); | |
85 | p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS); | |
95e300c0 CM |
86 | |
87 | info = kmalloc(sizeof(*info), GFP_KERNEL); | |
88 | if (!info) { | |
9abcfaaa | 89 | pr_warn("Could not allocate dmi info\n"); |
95e300c0 CM |
90 | } else { |
91 | info->si_type = si_type; | |
92 | info->flags = flags; | |
93 | info->addr = base_addr; | |
94 | info->slave_addr = slave_addr; | |
95 | info->next = ipmi_dmi_infos; | |
96 | ipmi_dmi_infos = info; | |
97 | } | |
98 | ||
9f88145f CM |
99 | pdev = platform_device_alloc(name, ipmi_dmi_nr); |
100 | if (!pdev) { | |
9abcfaaa | 101 | pr_err("Error allocation IPMI platform device\n"); |
9f88145f CM |
102 | return; |
103 | } | |
5516e21a JG |
104 | pdev->driver_override = kasprintf(GFP_KERNEL, "%s", |
105 | override); | |
106 | if (!pdev->driver_override) | |
107 | goto err; | |
9f88145f | 108 | |
95e300c0 | 109 | if (type == IPMI_DMI_TYPE_SSIF) { |
c5b24091 | 110 | p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr); |
9f88145f | 111 | goto add_properties; |
95e300c0 | 112 | } |
9f88145f CM |
113 | |
114 | memset(r, 0, sizeof(r)); | |
115 | ||
116 | r[0].start = base_addr; | |
117 | r[0].end = r[0].start + offset - 1; | |
118 | r[0].name = "IPMI Address 1"; | |
119 | r[0].flags = flags; | |
120 | ||
121 | if (size > 1) { | |
122 | r[1].start = r[0].start + offset; | |
123 | r[1].end = r[1].start + offset - 1; | |
124 | r[1].name = "IPMI Address 2"; | |
125 | r[1].flags = flags; | |
126 | num_r++; | |
127 | } | |
128 | ||
129 | if (size > 2) { | |
130 | r[2].start = r[1].start + offset; | |
131 | r[2].end = r[2].start + offset - 1; | |
132 | r[2].name = "IPMI Address 3"; | |
133 | r[2].flags = flags; | |
134 | num_r++; | |
135 | } | |
136 | ||
137 | if (irq) { | |
138 | r[num_r].start = irq; | |
139 | r[num_r].end = irq; | |
140 | r[num_r].name = "IPMI IRQ"; | |
141 | r[num_r].flags = IORESOURCE_IRQ; | |
142 | num_r++; | |
143 | } | |
144 | ||
145 | rv = platform_device_add_resources(pdev, r, num_r); | |
146 | if (rv) { | |
9abcfaaa | 147 | dev_err(&pdev->dev, "Unable to add resources: %d\n", rv); |
9f88145f CM |
148 | goto err; |
149 | } | |
150 | ||
151 | add_properties: | |
152 | rv = platform_device_add_properties(pdev, p); | |
153 | if (rv) { | |
9abcfaaa | 154 | dev_err(&pdev->dev, "Unable to add properties: %d\n", rv); |
9f88145f CM |
155 | goto err; |
156 | } | |
157 | ||
158 | rv = platform_device_add(pdev); | |
159 | if (rv) { | |
9abcfaaa | 160 | dev_err(&pdev->dev, "Unable to add device: %d\n", rv); |
9f88145f CM |
161 | goto err; |
162 | } | |
163 | ||
164 | ipmi_dmi_nr++; | |
165 | return; | |
166 | ||
167 | err: | |
168 | platform_device_put(pdev); | |
169 | } | |
170 | ||
171 | /* | |
172 | * Look up the slave address for a given interface. This is here | |
173 | * because ACPI doesn't have a slave address while SMBIOS does, but we | |
174 | * prefer using ACPI so the ACPI code can use the IPMI namespace. | |
175 | * This function allows an ACPI-specified IPMI device to look up the | |
176 | * slave address from the DMI table. | |
177 | */ | |
95e300c0 CM |
178 | int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, |
179 | unsigned long base_addr) | |
9f88145f CM |
180 | { |
181 | struct ipmi_dmi_info *info = ipmi_dmi_infos; | |
182 | ||
183 | while (info) { | |
95e300c0 | 184 | if (info->si_type == si_type && |
9f88145f CM |
185 | info->flags == flags && |
186 | info->addr == base_addr) | |
187 | return info->slave_addr; | |
188 | info = info->next; | |
189 | } | |
190 | ||
191 | return 0; | |
192 | } | |
193 | EXPORT_SYMBOL(ipmi_dmi_get_slave_addr); | |
194 | ||
195 | #define DMI_IPMI_MIN_LENGTH 0x10 | |
196 | #define DMI_IPMI_VER2_LENGTH 0x12 | |
197 | #define DMI_IPMI_TYPE 4 | |
198 | #define DMI_IPMI_SLAVEADDR 6 | |
199 | #define DMI_IPMI_ADDR 8 | |
200 | #define DMI_IPMI_ACCESS 0x10 | |
201 | #define DMI_IPMI_IRQ 0x11 | |
202 | #define DMI_IPMI_IO_MASK 0xfffe | |
203 | ||
204 | static void __init dmi_decode_ipmi(const struct dmi_header *dm) | |
205 | { | |
206 | const u8 *data = (const u8 *) dm; | |
207 | u32 flags = IORESOURCE_IO; | |
208 | unsigned long base_addr; | |
209 | u8 len = dm->length; | |
210 | u8 slave_addr; | |
211 | int irq = 0, offset; | |
212 | int type; | |
213 | ||
214 | if (len < DMI_IPMI_MIN_LENGTH) | |
215 | return; | |
216 | ||
217 | type = data[DMI_IPMI_TYPE]; | |
218 | slave_addr = data[DMI_IPMI_SLAVEADDR]; | |
219 | ||
220 | memcpy(&base_addr, data + DMI_IPMI_ADDR, sizeof(unsigned long)); | |
1574608f CM |
221 | if (!base_addr) { |
222 | pr_err("Base address is zero, assuming no IPMI interface\n"); | |
223 | return; | |
224 | } | |
9f88145f CM |
225 | if (len >= DMI_IPMI_VER2_LENGTH) { |
226 | if (type == IPMI_DMI_TYPE_SSIF) { | |
227 | offset = 0; | |
228 | flags = 0; | |
229 | base_addr = data[DMI_IPMI_ADDR] >> 1; | |
230 | if (base_addr == 0) { | |
231 | /* | |
232 | * Some broken systems put the I2C address in | |
233 | * the slave address field. We try to | |
234 | * accommodate them here. | |
235 | */ | |
236 | base_addr = data[DMI_IPMI_SLAVEADDR] >> 1; | |
237 | slave_addr = 0; | |
238 | } | |
239 | } else { | |
240 | if (base_addr & 1) { | |
241 | /* I/O */ | |
242 | base_addr &= DMI_IPMI_IO_MASK; | |
243 | } else { | |
244 | /* Memory */ | |
245 | flags = IORESOURCE_MEM; | |
246 | } | |
247 | ||
248 | /* | |
249 | * If bit 4 of byte 0x10 is set, then the lsb | |
250 | * for the address is odd. | |
251 | */ | |
252 | base_addr |= (data[DMI_IPMI_ACCESS] >> 4) & 1; | |
253 | ||
254 | irq = data[DMI_IPMI_IRQ]; | |
255 | ||
256 | /* | |
257 | * The top two bits of byte 0x10 hold the | |
258 | * register spacing. | |
259 | */ | |
260 | switch ((data[DMI_IPMI_ACCESS] >> 6) & 3) { | |
261 | case 0: /* Byte boundaries */ | |
262 | offset = 1; | |
263 | break; | |
264 | case 1: /* 32-bit boundaries */ | |
265 | offset = 4; | |
266 | break; | |
267 | case 2: /* 16-byte boundaries */ | |
268 | offset = 16; | |
269 | break; | |
270 | default: | |
9abcfaaa | 271 | pr_err("Invalid offset: 0\n"); |
9f88145f CM |
272 | return; |
273 | } | |
274 | } | |
275 | } else { | |
276 | /* Old DMI spec. */ | |
277 | /* | |
278 | * Note that technically, the lower bit of the base | |
279 | * address should be 1 if the address is I/O and 0 if | |
280 | * the address is in memory. So many systems get that | |
281 | * wrong (and all that I have seen are I/O) so we just | |
282 | * ignore that bit and assume I/O. Systems that use | |
283 | * memory should use the newer spec, anyway. | |
284 | */ | |
285 | base_addr = base_addr & DMI_IPMI_IO_MASK; | |
286 | offset = 1; | |
287 | } | |
288 | ||
289 | dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq, | |
290 | offset, type); | |
291 | } | |
292 | ||
293 | static int __init scan_for_dmi_ipmi(void) | |
294 | { | |
295 | const struct dmi_device *dev = NULL; | |
296 | ||
297 | while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) | |
298 | dmi_decode_ipmi((const struct dmi_header *) dev->device_data); | |
299 | ||
300 | return 0; | |
301 | } | |
302 | subsys_initcall(scan_for_dmi_ipmi); |