blob: 81aa8890b210de8bbf105ac5210bc89986fc9978 [file] [log] [blame]
Uwe Hermannb80dbf02007-04-22 19:08:13 +00001/*
Stefan Reinauer7e61e452008-01-18 10:35:56 +00002 * This file is part of the coreboot project.
Uwe Hermannb80dbf02007-04-22 19:08:13 +00003 *
Uwe Hermannb80dbf02007-04-22 19:08:13 +00004 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
Uwe Hermannb80dbf02007-04-22 19:08:13 +000012 */
Eric Biederman5cd81732004-03-11 15:01:31 +000013
14#include <console/console.h>
Eric Biederman5cd81732004-03-11 15:01:31 +000015#include <stdint.h>
Eric Biederman5cd81732004-03-11 15:01:31 +000016#include <arch/io.h>
17#include <device/device.h>
18#include <device/pnp.h>
19
Nico Huberdd4715b2013-06-10 22:08:35 +020020/* PNP config mode wrappers */
21
Elyes HAOUASb77cf622018-05-02 21:01:18 +020022void pnp_enter_conf_mode(struct device *dev)
Nico Huberdd4715b2013-06-10 22:08:35 +020023{
24 if (dev->ops->ops_pnp_mode)
25 dev->ops->ops_pnp_mode->enter_conf_mode(dev);
26}
27
Elyes HAOUASb77cf622018-05-02 21:01:18 +020028void pnp_exit_conf_mode(struct device *dev)
Nico Huberdd4715b2013-06-10 22:08:35 +020029{
30 if (dev->ops->ops_pnp_mode)
31 dev->ops->ops_pnp_mode->exit_conf_mode(dev);
32}
33
Patrick Rudolph7db16dd2019-12-10 13:15:42 +010034#if CONFIG(HAVE_ACPI_TABLES)
35void pnp_ssdt_enter_conf_mode(struct device *dev, const char *idx, const char *data)
36{
37 if (dev->ops->ops_pnp_mode && dev->ops->ops_pnp_mode->ssdt_enter_conf_mode)
38 dev->ops->ops_pnp_mode->ssdt_enter_conf_mode(dev, idx, data);
39}
40void pnp_ssdt_exit_conf_mode(struct device *dev, const char *idx, const char *data)
41{
42 if (dev->ops->ops_pnp_mode && dev->ops->ops_pnp_mode->ssdt_exit_conf_mode)
43 dev->ops->ops_pnp_mode->ssdt_exit_conf_mode(dev, idx, data);
44}
45#endif
46
Eric Biederman5cd81732004-03-11 15:01:31 +000047/* PNP fundamental operations */
48
Elyes HAOUASb77cf622018-05-02 21:01:18 +020049void pnp_write_config(struct device *dev, u8 reg, u8 value)
Eric Biederman5cd81732004-03-11 15:01:31 +000050{
Stefan Reinauer2b34db82009-02-28 20:10:20 +000051 outb(reg, dev->path.pnp.port);
52 outb(value, dev->path.pnp.port + 1);
Eric Biederman5cd81732004-03-11 15:01:31 +000053}
54
Elyes HAOUASb77cf622018-05-02 21:01:18 +020055u8 pnp_read_config(struct device *dev, u8 reg)
Eric Biederman5cd81732004-03-11 15:01:31 +000056{
Stefan Reinauer2b34db82009-02-28 20:10:20 +000057 outb(reg, dev->path.pnp.port);
58 return inb(dev->path.pnp.port + 1);
Eric Biederman5cd81732004-03-11 15:01:31 +000059}
60
Elyes HAOUASb77cf622018-05-02 21:01:18 +020061void pnp_set_logical_device(struct device *dev)
Eric Biederman5cd81732004-03-11 15:01:31 +000062{
Stefan Reinauer2b34db82009-02-28 20:10:20 +000063 pnp_write_config(dev, 0x07, dev->path.pnp.device & 0xff);
Eric Biederman5cd81732004-03-11 15:01:31 +000064}
65
Elyes HAOUASb77cf622018-05-02 21:01:18 +020066void pnp_set_enable(struct device *dev, int enable)
Eric Biederman5cd81732004-03-11 15:01:31 +000067{
Uwe Hermannd453dd02010-10-18 00:00:57 +000068 u8 tmp, bitpos;
Rudolf Marek623df672008-02-18 20:32:46 +000069
70 tmp = pnp_read_config(dev, 0x30);
Uwe Hermannd453dd02010-10-18 00:00:57 +000071
72 /* Handle virtual devices, which share the same LDN register. */
Stefan Reinauer2b34db82009-02-28 20:10:20 +000073 bitpos = (dev->path.pnp.device >> 8) & 0x7;
Rudolf Marek623df672008-02-18 20:32:46 +000074
Uwe Hermannd453dd02010-10-18 00:00:57 +000075 if (enable)
Rudolf Marek623df672008-02-18 20:32:46 +000076 tmp |= (1 << bitpos);
Uwe Hermannd453dd02010-10-18 00:00:57 +000077 else
Rudolf Marek623df672008-02-18 20:32:46 +000078 tmp &= ~(1 << bitpos);
Uwe Hermannd453dd02010-10-18 00:00:57 +000079
Rudolf Marek623df672008-02-18 20:32:46 +000080 pnp_write_config(dev, 0x30, tmp);
Eric Biederman5cd81732004-03-11 15:01:31 +000081}
82
Elyes HAOUASb77cf622018-05-02 21:01:18 +020083int pnp_read_enable(struct device *dev)
Eric Biederman5cd81732004-03-11 15:01:31 +000084{
Uwe Hermannd453dd02010-10-18 00:00:57 +000085 u8 tmp, bitpos;
86
Rudolf Marek623df672008-02-18 20:32:46 +000087 tmp = pnp_read_config(dev, 0x30);
Uwe Hermannd453dd02010-10-18 00:00:57 +000088
89 /* Handle virtual devices, which share the same LDN register. */
Stefan Reinauer2b34db82009-02-28 20:10:20 +000090 bitpos = (dev->path.pnp.device >> 8) & 0x7;
Uwe Hermannd453dd02010-10-18 00:00:57 +000091
Rudolf Marekb05d4bb2008-02-19 20:30:25 +000092 return !!(tmp & (1 << bitpos));
Eric Biederman5cd81732004-03-11 15:01:31 +000093}
94
Elyes HAOUASb77cf622018-05-02 21:01:18 +020095void pnp_set_iobase(struct device *dev, u8 index, u16 iobase)
Eric Biederman5cd81732004-03-11 15:01:31 +000096{
Uwe Hermannd453dd02010-10-18 00:00:57 +000097 /* Index == 0x60 or 0x62. */
Eric Biederman5cd81732004-03-11 15:01:31 +000098 pnp_write_config(dev, index + 0, (iobase >> 8) & 0xff);
99 pnp_write_config(dev, index + 1, iobase & 0xff);
100}
101
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200102void pnp_set_irq(struct device *dev, u8 index, u8 irq)
Eric Biederman5cd81732004-03-11 15:01:31 +0000103{
Uwe Hermannd453dd02010-10-18 00:00:57 +0000104 /* Index == 0x70 or 0x72. */
Eric Biederman5cd81732004-03-11 15:01:31 +0000105 pnp_write_config(dev, index, irq);
106}
107
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200108void pnp_set_drq(struct device *dev, u8 index, u8 drq)
Eric Biederman5cd81732004-03-11 15:01:31 +0000109{
Uwe Hermannd453dd02010-10-18 00:00:57 +0000110 /* Index == 0x74. */
Eric Biederman5cd81732004-03-11 15:01:31 +0000111 pnp_write_config(dev, index, drq & 0xff);
112}
113
114/* PNP device operations */
115
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200116void pnp_read_resources(struct device *dev)
Eric Biederman5cd81732004-03-11 15:01:31 +0000117{
118 return;
119}
120
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200121static void pnp_set_resource(struct device *dev, struct resource *resource)
Eric Biederman5cd81732004-03-11 15:01:31 +0000122{
123 if (!(resource->flags & IORESOURCE_ASSIGNED)) {
Elyes HAOUAS495bb662019-09-23 13:15:41 +0200124 /* The PNP_MSC Super IO registers have the IRQ flag set. If no
Felix Helda6627772018-07-27 16:41:32 +0200125 value is assigned in the devicetree, the corresponding
126 PNP_MSC register doesn't get written, which should be printed
127 as warning and not as error. */
128 if (resource->flags & IORESOURCE_IRQ &&
129 (resource->index != PNP_IDX_IRQ0) &&
130 (resource->index != PNP_IDX_IRQ1))
131 printk(BIOS_WARNING, "WARNING: %s %02lx %s size: "
132 "0x%010llx not assigned\n", dev_path(dev),
133 resource->index, resource_type(resource),
134 resource->size);
135 else
136 printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx "
137 "not assigned\n", dev_path(dev), resource->index,
138 resource_type(resource), resource->size);
Eric Biederman5cd81732004-03-11 15:01:31 +0000139 return;
140 }
Li-Ta Lo75337f72004-05-07 21:56:48 +0000141
Uwe Hermannd453dd02010-10-18 00:00:57 +0000142 /* Now store the resource. */
Eric Biederman5cd81732004-03-11 15:01:31 +0000143 if (resource->flags & IORESOURCE_IO) {
144 pnp_set_iobase(dev, resource->index, resource->base);
Uwe Hermannd453dd02010-10-18 00:00:57 +0000145 } else if (resource->flags & IORESOURCE_DRQ) {
Eric Biederman5cd81732004-03-11 15:01:31 +0000146 pnp_set_drq(dev, resource->index, resource->base);
Uwe Hermannd453dd02010-10-18 00:00:57 +0000147 } else if (resource->flags & IORESOURCE_IRQ) {
Eric Biederman5cd81732004-03-11 15:01:31 +0000148 pnp_set_irq(dev, resource->index, resource->base);
Uwe Hermannd453dd02010-10-18 00:00:57 +0000149 } else {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000150 printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
Uwe Hermannd453dd02010-10-18 00:00:57 +0000151 dev_path(dev), resource->index);
Eric Biederman5cd81732004-03-11 15:01:31 +0000152 return;
153 }
Li-Ta Lo9f0d0f92004-05-10 16:05:16 +0000154 resource->flags |= IORESOURCE_STORED;
Eric Biederman5cd81732004-03-11 15:01:31 +0000155
Eric Biederman03acab62004-10-14 21:25:53 +0000156 report_resource_stored(dev, resource, "");
Eric Biederman5cd81732004-03-11 15:01:31 +0000157}
158
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200159void pnp_set_resources(struct device *dev)
Eric Biederman5cd81732004-03-11 15:01:31 +0000160{
Myles Watsonc25cc112010-05-21 14:33:48 +0000161 struct resource *res;
Eric Biederman5cd81732004-03-11 15:01:31 +0000162
Nico Huberdd4715b2013-06-10 22:08:35 +0200163 pnp_enter_conf_mode(dev);
164
Uwe Hermannd453dd02010-10-18 00:00:57 +0000165 /* Select the logical device (LDN). */
Eric Biederman5cd81732004-03-11 15:01:31 +0000166 pnp_set_logical_device(dev);
167
168 /* Paranoia says I should disable the device here... */
Uwe Hermannd453dd02010-10-18 00:00:57 +0000169 for (res = dev->resource_list; res; res = res->next)
Myles Watsonc25cc112010-05-21 14:33:48 +0000170 pnp_set_resource(dev, res);
Nico Huberdd4715b2013-06-10 22:08:35 +0200171
172 pnp_exit_conf_mode(dev);
Eric Biederman5cd81732004-03-11 15:01:31 +0000173}
174
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200175void pnp_enable_resources(struct device *dev)
Eric Biederman5cd81732004-03-11 15:01:31 +0000176{
Nico Huberdd4715b2013-06-10 22:08:35 +0200177 pnp_enter_conf_mode(dev);
Eric Biederman5cd81732004-03-11 15:01:31 +0000178 pnp_set_logical_device(dev);
179 pnp_set_enable(dev, 1);
Nico Huberdd4715b2013-06-10 22:08:35 +0200180 pnp_exit_conf_mode(dev);
Eric Biederman5cd81732004-03-11 15:01:31 +0000181}
182
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200183void pnp_enable(struct device *dev)
Eric Biederman5cd81732004-03-11 15:01:31 +0000184{
Li-Ta Lo75337f72004-05-07 21:56:48 +0000185 if (!dev->enabled) {
Nico Huberdd4715b2013-06-10 22:08:35 +0200186 pnp_enter_conf_mode(dev);
Li-Ta Loc6bcedb2004-04-21 16:57:05 +0000187 pnp_set_logical_device(dev);
Eric Biederman5cd81732004-03-11 15:01:31 +0000188 pnp_set_enable(dev, 0);
Nico Huberdd4715b2013-06-10 22:08:35 +0200189 pnp_exit_conf_mode(dev);
Eric Biederman5cd81732004-03-11 15:01:31 +0000190 }
191}
192
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200193void pnp_alt_enable(struct device *dev)
Nico Huberf898f7b2013-06-10 22:57:12 +0200194{
Nico Huberdd4715b2013-06-10 22:08:35 +0200195 pnp_enter_conf_mode(dev);
Nico Huberf898f7b2013-06-10 22:57:12 +0200196 pnp_set_logical_device(dev);
197 pnp_set_enable(dev, !!dev->enabled);
Nico Huberdd4715b2013-06-10 22:08:35 +0200198 pnp_exit_conf_mode(dev);
Nico Huberf898f7b2013-06-10 22:57:12 +0200199}
200
Eric Biederman5cd81732004-03-11 15:01:31 +0000201struct device_operations pnp_ops = {
202 .read_resources = pnp_read_resources,
203 .set_resources = pnp_set_resources,
204 .enable_resources = pnp_enable_resources,
205 .enable = pnp_enable,
206};
207
Uwe Hermanne4870472010-11-04 23:23:47 +0000208/* PNP chip operations */
Eric Biederman5cd81732004-03-11 15:01:31 +0000209
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200210static void pnp_get_ioresource(struct device *dev, u8 index, u16 mask)
Eric Biederman5cd81732004-03-11 15:01:31 +0000211{
212 struct resource *resource;
Felix Heldf2ec6482018-07-27 02:31:09 +0200213 unsigned int bit;
Li-Ta Lo75337f72004-05-07 21:56:48 +0000214
Felix Heldf2ec6482018-07-27 02:31:09 +0200215 /* If none of the mask bits is set, the resource would occupy the whole
216 IO space leading to IO resource conflicts with the other devices */
Samuel Holland7daac912017-06-06 22:55:01 -0500217 if (!mask) {
Stefan Reinaueraeead272011-01-31 21:16:48 +0000218 printk(BIOS_ERR, "ERROR: device %s index %d has no mask.\n",
219 dev_path(dev), index);
220 return;
221 }
222
Eric Biederman03acab62004-10-14 21:25:53 +0000223 resource = new_resource(dev, index);
Eric Biederman5cd81732004-03-11 15:01:31 +0000224 resource->flags |= IORESOURCE_IO;
Stefan Reinauer14e22772010-04-27 06:56:47 +0000225
Felix Heldf2ec6482018-07-27 02:31:09 +0200226 /* Calculate IO region size which is determined by the first one from
227 the LSB of the mask. */
228 for (bit = 0; bit <= 15 && (mask & (1 << bit)) == 0; ++bit)
229 ;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000230
Felix Heldf2ec6482018-07-27 02:31:09 +0200231 resource->gran = bit;
232 resource->align = bit;
233 resource->size = 1 << bit;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000234
Felix Heldf2ec6482018-07-27 02:31:09 +0200235 /* Calculate IO region address limit which is determined by the first
236 one from the MSB of the mask. */
237 for (bit = 15; bit != 0 && (mask & (1 << bit)) == 0; --bit)
238 ;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000239
Felix Heldf2ec6482018-07-27 02:31:09 +0200240 resource->limit = (1 << (bit + 1)) - 1;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000241
Felix Heldf2ec6482018-07-27 02:31:09 +0200242 /* The block of ones in the mask is expected to be continuous.
243 If there is any zero inbetween the block of ones, it is ignored
244 in the calculation of the resource size and limit. */
245 if (mask != (resource->limit ^ (resource->size - 1)))
246 printk(BIOS_WARNING,
247 "WARNING: mask of device %s index %d is wrong.\n",
248 dev_path(dev), index);
Eric Biederman5cd81732004-03-11 15:01:31 +0000249}
250
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200251static void get_resources(struct device *dev, struct pnp_info *info)
Eric Biederman5cd81732004-03-11 15:01:31 +0000252{
253 struct resource *resource;
254
Uwe Hermanne4870472010-11-04 23:23:47 +0000255 if (info->flags & PNP_IO0)
Samuel Holland7daac912017-06-06 22:55:01 -0500256 pnp_get_ioresource(dev, PNP_IDX_IO0, info->io0);
Uwe Hermanne4870472010-11-04 23:23:47 +0000257 if (info->flags & PNP_IO1)
Samuel Holland7daac912017-06-06 22:55:01 -0500258 pnp_get_ioresource(dev, PNP_IDX_IO1, info->io1);
Uwe Hermanne4870472010-11-04 23:23:47 +0000259 if (info->flags & PNP_IO2)
Samuel Holland7daac912017-06-06 22:55:01 -0500260 pnp_get_ioresource(dev, PNP_IDX_IO2, info->io2);
Uwe Hermanne4870472010-11-04 23:23:47 +0000261 if (info->flags & PNP_IO3)
Samuel Holland7daac912017-06-06 22:55:01 -0500262 pnp_get_ioresource(dev, PNP_IDX_IO3, info->io3);
Uwe Hermanne4870472010-11-04 23:23:47 +0000263
Eric Biederman5cd81732004-03-11 15:01:31 +0000264 if (info->flags & PNP_IRQ0) {
Eric Biederman03acab62004-10-14 21:25:53 +0000265 resource = new_resource(dev, PNP_IDX_IRQ0);
Eric Biederman5cd81732004-03-11 15:01:31 +0000266 resource->size = 1;
267 resource->flags |= IORESOURCE_IRQ;
268 }
269 if (info->flags & PNP_IRQ1) {
Eric Biederman03acab62004-10-14 21:25:53 +0000270 resource = new_resource(dev, PNP_IDX_IRQ1);
Eric Biederman5cd81732004-03-11 15:01:31 +0000271 resource->size = 1;
272 resource->flags |= IORESOURCE_IRQ;
273 }
Uwe Hermanne4870472010-11-04 23:23:47 +0000274
Eric Biederman5cd81732004-03-11 15:01:31 +0000275 if (info->flags & PNP_DRQ0) {
Eric Biederman03acab62004-10-14 21:25:53 +0000276 resource = new_resource(dev, PNP_IDX_DRQ0);
Eric Biederman5cd81732004-03-11 15:01:31 +0000277 resource->size = 1;
278 resource->flags |= IORESOURCE_DRQ;
279 }
280 if (info->flags & PNP_DRQ1) {
Eric Biederman03acab62004-10-14 21:25:53 +0000281 resource = new_resource(dev, PNP_IDX_DRQ1);
Eric Biederman5cd81732004-03-11 15:01:31 +0000282 resource->size = 1;
283 resource->flags |= IORESOURCE_DRQ;
Stefan Reinauerde3206a2010-02-22 06:09:43 +0000284 }
Uwe Hermannd453dd02010-10-18 00:00:57 +0000285
286 /*
287 * These are not IRQs, but set the flag to have the
288 * resource allocator do the right thing.
Stefan Reinauerde3206a2010-02-22 06:09:43 +0000289 */
290 if (info->flags & PNP_EN) {
291 resource = new_resource(dev, PNP_IDX_EN);
292 resource->size = 1;
293 resource->flags |= IORESOURCE_IRQ;
294 }
295 if (info->flags & PNP_MSC0) {
296 resource = new_resource(dev, PNP_IDX_MSC0);
297 resource->size = 1;
298 resource->flags |= IORESOURCE_IRQ;
299 }
300 if (info->flags & PNP_MSC1) {
301 resource = new_resource(dev, PNP_IDX_MSC1);
302 resource->size = 1;
303 resource->flags |= IORESOURCE_IRQ;
304 }
Damien Zammit62c02762015-12-28 23:04:47 +1100305 if (info->flags & PNP_MSC2) {
306 resource = new_resource(dev, PNP_IDX_MSC2);
307 resource->size = 1;
308 resource->flags |= IORESOURCE_IRQ;
309 }
310 if (info->flags & PNP_MSC3) {
311 resource = new_resource(dev, PNP_IDX_MSC3);
312 resource->size = 1;
313 resource->flags |= IORESOURCE_IRQ;
314 }
Stefan Reinauer0108b932013-12-11 11:06:08 -0800315 if (info->flags & PNP_MSC4) {
316 resource = new_resource(dev, PNP_IDX_MSC4);
317 resource->size = 1;
318 resource->flags |= IORESOURCE_IRQ;
319 }
Damien Zammit62c02762015-12-28 23:04:47 +1100320 if (info->flags & PNP_MSC5) {
321 resource = new_resource(dev, PNP_IDX_MSC5);
322 resource->size = 1;
323 resource->flags |= IORESOURCE_IRQ;
324 }
325 if (info->flags & PNP_MSC6) {
326 resource = new_resource(dev, PNP_IDX_MSC6);
327 resource->size = 1;
328 resource->flags |= IORESOURCE_IRQ;
329 }
330 if (info->flags & PNP_MSC7) {
331 resource = new_resource(dev, PNP_IDX_MSC7);
332 resource->size = 1;
333 resource->flags |= IORESOURCE_IRQ;
334 }
335 if (info->flags & PNP_MSC8) {
336 resource = new_resource(dev, PNP_IDX_MSC8);
337 resource->size = 1;
338 resource->flags |= IORESOURCE_IRQ;
339 }
340 if (info->flags & PNP_MSC9) {
341 resource = new_resource(dev, PNP_IDX_MSC9);
342 resource->size = 1;
343 resource->flags |= IORESOURCE_IRQ;
344 }
345 if (info->flags & PNP_MSCA) {
346 resource = new_resource(dev, PNP_IDX_MSCA);
347 resource->size = 1;
348 resource->flags |= IORESOURCE_IRQ;
349 }
350 if (info->flags & PNP_MSCB) {
351 resource = new_resource(dev, PNP_IDX_MSCB);
352 resource->size = 1;
353 resource->flags |= IORESOURCE_IRQ;
354 }
355 if (info->flags & PNP_MSCC) {
356 resource = new_resource(dev, PNP_IDX_MSCC);
357 resource->size = 1;
358 resource->flags |= IORESOURCE_IRQ;
359 }
360 if (info->flags & PNP_MSCD) {
361 resource = new_resource(dev, PNP_IDX_MSCD);
362 resource->size = 1;
363 resource->flags |= IORESOURCE_IRQ;
364 }
365 if (info->flags & PNP_MSCE) {
366 resource = new_resource(dev, PNP_IDX_MSCE);
Stefan Reinauer08ba7d02013-10-31 15:58:50 -0700367 resource->size = 1;
368 resource->flags |= IORESOURCE_IRQ;
369 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000370}
Eric Biederman5cd81732004-03-11 15:01:31 +0000371
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200372void pnp_enable_devices(struct device *base_dev, struct device_operations *ops,
Uwe Hermannd453dd02010-10-18 00:00:57 +0000373 unsigned int functions, struct pnp_info *info)
Eric Biederman5cd81732004-03-11 15:01:31 +0000374{
375 struct device_path path;
Elyes HAOUASb77cf622018-05-02 21:01:18 +0200376 struct device *dev;
Eric Biederman5cd81732004-03-11 15:01:31 +0000377 int i;
378
Uwe Hermannd453dd02010-10-18 00:00:57 +0000379 path.type = DEVICE_PATH_PNP;
Stefan Reinauer2b34db82009-02-28 20:10:20 +0000380 path.pnp.port = base_dev->path.pnp.port;
Stefan Reinauer14e22772010-04-27 06:56:47 +0000381
Uwe Hermannd453dd02010-10-18 00:00:57 +0000382 /* Setup the ops and resources on the newly allocated devices. */
383 for (i = 0; i < functions; i++) {
Uwe Hermannaa4bedf2007-07-12 13:12:47 +0000384 /* Skip logical devices this Super I/O doesn't have. */
Felix Held7b7bc592019-12-15 13:53:48 +0100385 if (info[i].function == PNP_SKIP_FUNCTION)
Uwe Hermannaa4bedf2007-07-12 13:12:47 +0000386 continue;
387
Stefan Reinauer2b34db82009-02-28 20:10:20 +0000388 path.pnp.device = info[i].function;
Eric Biederman7003ba42004-10-16 06:20:29 +0000389 dev = alloc_find_dev(base_dev->bus, &path);
Stefan Reinauer14e22772010-04-27 06:56:47 +0000390
Uwe Hermannd453dd02010-10-18 00:00:57 +0000391 /* Don't initialize a device multiple times. */
Stefan Reinauer14e22772010-04-27 06:56:47 +0000392 if (dev->ops)
Eric Biederman7003ba42004-10-16 06:20:29 +0000393 continue;
Li-Ta Loc6bcedb2004-04-21 16:57:05 +0000394
Felix Held745e58a2018-07-06 19:38:24 +0200395 /* use LDN-specific ops override from corresponding pnp_info
396 entry if not NULL */
397 if (info[i].ops)
Li-Ta Lo75337f72004-05-07 21:56:48 +0000398 dev->ops = info[i].ops;
Felix Held745e58a2018-07-06 19:38:24 +0200399 /* else use device ops */
400 else
401 dev->ops = ops;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000402
Eric Biederman5cd81732004-03-11 15:01:31 +0000403 get_resources(dev, &info[i]);
Eric Biederman5cd81732004-03-11 15:01:31 +0000404 }
405}