Remove address from GPLv2 headers
[coreboot.git] / src / device / pnp_device.c
blob2ffe193af965986ad7b11e908f11cf3d21fb5738
1 /*
2 * This file is part of the coreboot project.
4 * Copyright (C) 2004 Linux Networx
5 * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
6 * Copyright (C) 2004 Li-Ta Lo <ollie@lanl.gov>
7 * Copyright (C) 2005 Tyan
8 * (Written by Yinghai Lu <yhlu@tyan.com> for Tyan)
9 * Copyright (C) 2013 Nico Huber <nico.h@gmx.de>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc.
25 #include <console/console.h>
26 #include <stdlib.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <arch/io.h>
30 #include <device/device.h>
31 #include <device/pnp.h>
33 /* PNP config mode wrappers */
35 void pnp_enter_conf_mode(device_t dev)
37 if (dev->ops->ops_pnp_mode)
38 dev->ops->ops_pnp_mode->enter_conf_mode(dev);
41 void pnp_exit_conf_mode(device_t dev)
43 if (dev->ops->ops_pnp_mode)
44 dev->ops->ops_pnp_mode->exit_conf_mode(dev);
47 /* PNP fundamental operations */
49 void pnp_write_config(device_t dev, u8 reg, u8 value)
51 outb(reg, dev->path.pnp.port);
52 outb(value, dev->path.pnp.port + 1);
55 u8 pnp_read_config(device_t dev, u8 reg)
57 outb(reg, dev->path.pnp.port);
58 return inb(dev->path.pnp.port + 1);
61 void pnp_set_logical_device(device_t dev)
63 pnp_write_config(dev, 0x07, dev->path.pnp.device & 0xff);
66 void pnp_set_enable(device_t dev, int enable)
68 u8 tmp, bitpos;
70 tmp = pnp_read_config(dev, 0x30);
72 /* Handle virtual devices, which share the same LDN register. */
73 bitpos = (dev->path.pnp.device >> 8) & 0x7;
75 if (enable)
76 tmp |= (1 << bitpos);
77 else
78 tmp &= ~(1 << bitpos);
80 pnp_write_config(dev, 0x30, tmp);
83 int pnp_read_enable(device_t dev)
85 u8 tmp, bitpos;
87 tmp = pnp_read_config(dev, 0x30);
89 /* Handle virtual devices, which share the same LDN register. */
90 bitpos = (dev->path.pnp.device >> 8) & 0x7;
92 return !!(tmp & (1 << bitpos));
95 void pnp_set_iobase(device_t dev, u8 index, u16 iobase)
97 /* Index == 0x60 or 0x62. */
98 pnp_write_config(dev, index + 0, (iobase >> 8) & 0xff);
99 pnp_write_config(dev, index + 1, iobase & 0xff);
102 void pnp_set_irq(device_t dev, u8 index, u8 irq)
104 /* Index == 0x70 or 0x72. */
105 pnp_write_config(dev, index, irq);
108 void pnp_set_drq(device_t dev, u8 index, u8 drq)
110 /* Index == 0x74. */
111 pnp_write_config(dev, index, drq & 0xff);
114 /* PNP device operations */
116 void pnp_read_resources(device_t dev)
118 return;
121 static void pnp_set_resource(device_t dev, struct resource *resource)
123 if (!(resource->flags & IORESOURCE_ASSIGNED)) {
124 printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx "
125 "not assigned\n", dev_path(dev), resource->index,
126 resource_type(resource), resource->size);
127 return;
130 /* Now store the resource. */
131 if (resource->flags & IORESOURCE_IO) {
132 pnp_set_iobase(dev, resource->index, resource->base);
133 } else if (resource->flags & IORESOURCE_DRQ) {
134 pnp_set_drq(dev, resource->index, resource->base);
135 } else if (resource->flags & IORESOURCE_IRQ) {
136 pnp_set_irq(dev, resource->index, resource->base);
137 } else {
138 printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
139 dev_path(dev), resource->index);
140 return;
142 resource->flags |= IORESOURCE_STORED;
144 report_resource_stored(dev, resource, "");
147 void pnp_set_resources(device_t dev)
149 struct resource *res;
151 pnp_enter_conf_mode(dev);
153 /* Select the logical device (LDN). */
154 pnp_set_logical_device(dev);
156 /* Paranoia says I should disable the device here... */
157 for (res = dev->resource_list; res; res = res->next)
158 pnp_set_resource(dev, res);
160 pnp_exit_conf_mode(dev);
163 void pnp_enable_resources(device_t dev)
165 pnp_enter_conf_mode(dev);
166 pnp_set_logical_device(dev);
167 pnp_set_enable(dev, 1);
168 pnp_exit_conf_mode(dev);
171 void pnp_enable(device_t dev)
173 if (!dev->enabled) {
174 pnp_enter_conf_mode(dev);
175 pnp_set_logical_device(dev);
176 pnp_set_enable(dev, 0);
177 pnp_exit_conf_mode(dev);
181 void pnp_alt_enable(device_t dev)
183 pnp_enter_conf_mode(dev);
184 pnp_set_logical_device(dev);
185 pnp_set_enable(dev, !!dev->enabled);
186 pnp_exit_conf_mode(dev);
189 struct device_operations pnp_ops = {
190 .read_resources = pnp_read_resources,
191 .set_resources = pnp_set_resources,
192 .enable_resources = pnp_enable_resources,
193 .enable = pnp_enable,
196 /* PNP chip operations */
198 static void pnp_get_ioresource(device_t dev, u8 index, struct io_info *info)
200 struct resource *resource;
201 unsigned moving, gran, step;
203 if (!info->mask) {
204 printk(BIOS_ERR, "ERROR: device %s index %d has no mask.\n",
205 dev_path(dev), index);
206 return;
209 resource = new_resource(dev, index);
211 /* Initilize the resource. */
212 resource->limit = 0xffff;
213 resource->flags |= IORESOURCE_IO;
215 /* Get the resource size... */
217 moving = info->mask;
218 gran = 15;
219 step = 1 << gran;
221 /* Find the first bit that moves. */
222 while ((moving & step) == 0) {
223 gran--;
224 step >>= 1;
227 /* Now find the first bit that does not move. */
228 while ((moving & step) != 0) {
229 gran--;
230 step >>= 1;
234 * Of the moving bits the last bit in the first group,
235 * tells us the size of this resource.
237 if ((moving & step) == 0) {
238 gran++;
239 step <<= 1;
242 /* Set the resource size and alignment. */
243 resource->gran = gran;
244 resource->align = gran;
245 resource->limit = info->mask | (step - 1);
246 resource->size = 1 << gran;
249 static void get_resources(device_t dev, struct pnp_info *info)
251 struct resource *resource;
253 if (info->flags & PNP_IO0)
254 pnp_get_ioresource(dev, PNP_IDX_IO0, &info->io0);
255 if (info->flags & PNP_IO1)
256 pnp_get_ioresource(dev, PNP_IDX_IO1, &info->io1);
257 if (info->flags & PNP_IO2)
258 pnp_get_ioresource(dev, PNP_IDX_IO2, &info->io2);
259 if (info->flags & PNP_IO3)
260 pnp_get_ioresource(dev, PNP_IDX_IO3, &info->io3);
262 if (info->flags & PNP_IRQ0) {
263 resource = new_resource(dev, PNP_IDX_IRQ0);
264 resource->size = 1;
265 resource->flags |= IORESOURCE_IRQ;
267 if (info->flags & PNP_IRQ1) {
268 resource = new_resource(dev, PNP_IDX_IRQ1);
269 resource->size = 1;
270 resource->flags |= IORESOURCE_IRQ;
273 if (info->flags & PNP_DRQ0) {
274 resource = new_resource(dev, PNP_IDX_DRQ0);
275 resource->size = 1;
276 resource->flags |= IORESOURCE_DRQ;
278 if (info->flags & PNP_DRQ1) {
279 resource = new_resource(dev, PNP_IDX_DRQ1);
280 resource->size = 1;
281 resource->flags |= IORESOURCE_DRQ;
285 * These are not IRQs, but set the flag to have the
286 * resource allocator do the right thing.
288 if (info->flags & PNP_EN) {
289 resource = new_resource(dev, PNP_IDX_EN);
290 resource->size = 1;
291 resource->flags |= IORESOURCE_IRQ;
293 if (info->flags & PNP_MSC0) {
294 resource = new_resource(dev, PNP_IDX_MSC0);
295 resource->size = 1;
296 resource->flags |= IORESOURCE_IRQ;
298 if (info->flags & PNP_MSC1) {
299 resource = new_resource(dev, PNP_IDX_MSC1);
300 resource->size = 1;
301 resource->flags |= IORESOURCE_IRQ;
303 if (info->flags & PNP_MSC4) {
304 resource = new_resource(dev, PNP_IDX_MSC4);
305 resource->size = 1;
306 resource->flags |= IORESOURCE_IRQ;
308 if (info->flags & PNP_MSC10) {
309 resource = new_resource(dev, PNP_IDX_MSC10);
310 resource->size = 1;
311 resource->flags |= IORESOURCE_IRQ;
315 void pnp_enable_devices(device_t base_dev, struct device_operations *ops,
316 unsigned int functions, struct pnp_info *info)
318 struct device_path path;
319 device_t dev;
320 int i;
322 path.type = DEVICE_PATH_PNP;
323 path.pnp.port = base_dev->path.pnp.port;
325 /* Setup the ops and resources on the newly allocated devices. */
326 for (i = 0; i < functions; i++) {
327 /* Skip logical devices this Super I/O doesn't have. */
328 if (info[i].function == -1)
329 continue;
331 path.pnp.device = info[i].function;
332 dev = alloc_find_dev(base_dev->bus, &path);
334 /* Don't initialize a device multiple times. */
335 if (dev->ops)
336 continue;
338 if (info[i].ops == 0)
339 dev->ops = ops;
340 else
341 dev->ops = info[i].ops;
343 get_resources(dev, &info[i]);