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