blob: 61814256739100b1e204a5e3e9a367f178c915c7 [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 *
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 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
Eric Biederman5cd81732004-03-11 15:01:31 +000023
24#include <console/console.h>
25#include <stdlib.h>
26#include <stdint.h>
27#include <bitops.h>
28#include <string.h>
29#include <arch/io.h>
30#include <device/device.h>
31#include <device/pnp.h>
32
Eric Biederman5cd81732004-03-11 15:01:31 +000033/* PNP fundamental operations */
34
35void pnp_write_config(device_t dev, uint8_t reg, uint8_t value)
36{
Stefan Reinauer2b34db82009-02-28 20:10:20 +000037 outb(reg, dev->path.pnp.port);
38 outb(value, dev->path.pnp.port + 1);
Eric Biederman5cd81732004-03-11 15:01:31 +000039}
40
41uint8_t pnp_read_config(device_t dev, uint8_t reg)
42{
Stefan Reinauer2b34db82009-02-28 20:10:20 +000043 outb(reg, dev->path.pnp.port);
44 return inb(dev->path.pnp.port + 1);
Eric Biederman5cd81732004-03-11 15:01:31 +000045}
46
47void pnp_set_logical_device(device_t dev)
48{
Stefan Reinauer2b34db82009-02-28 20:10:20 +000049 pnp_write_config(dev, 0x07, dev->path.pnp.device & 0xff);
Eric Biederman5cd81732004-03-11 15:01:31 +000050}
51
52void pnp_set_enable(device_t dev, int enable)
53{
Rudolf Marekb05d4bb2008-02-19 20:30:25 +000054 uint8_t tmp, bitpos;
Rudolf Marek623df672008-02-18 20:32:46 +000055
56 tmp = pnp_read_config(dev, 0x30);
57 /* handle the virtual devices, which share same LDN register */
Stefan Reinauer2b34db82009-02-28 20:10:20 +000058 bitpos = (dev->path.pnp.device >> 8) & 0x7;
Rudolf Marek623df672008-02-18 20:32:46 +000059
60 if (enable) {
61 tmp |= (1 << bitpos);
62 } else {
63 tmp &= ~(1 << bitpos);
64 }
65 pnp_write_config(dev, 0x30, tmp);
Eric Biederman5cd81732004-03-11 15:01:31 +000066}
67
68int pnp_read_enable(device_t dev)
69{
Rudolf Marekb05d4bb2008-02-19 20:30:25 +000070 uint8_t tmp, bitpos;
Rudolf Marek623df672008-02-18 20:32:46 +000071 tmp = pnp_read_config(dev, 0x30);
72 /* handle the virtual devices, which share same LDN register */
Stefan Reinauer2b34db82009-02-28 20:10:20 +000073 bitpos = (dev->path.pnp.device >> 8) & 0x7;
Rudolf Marekb05d4bb2008-02-19 20:30:25 +000074 return !!(tmp & (1 << bitpos));
Eric Biederman5cd81732004-03-11 15:01:31 +000075}
76
77void pnp_set_iobase(device_t dev, unsigned index, unsigned iobase)
78{
79 /* Index == 0x60 or 0x62 */
80 pnp_write_config(dev, index + 0, (iobase >> 8) & 0xff);
81 pnp_write_config(dev, index + 1, iobase & 0xff);
82}
83
84void pnp_set_irq(device_t dev, unsigned index, unsigned irq)
85{
86 /* Index == 0x70 or 0x72 */
87 pnp_write_config(dev, index, irq);
88}
89
Steven J. Magnani740bb2a2005-09-09 20:02:52 +000090void pnp_set_drq(device_t dev, unsigned index, unsigned drq)
Eric Biederman5cd81732004-03-11 15:01:31 +000091{
92 /* Index == 0x74 */
93 pnp_write_config(dev, index, drq & 0xff);
94}
95
96/* PNP device operations */
97
98void pnp_read_resources(device_t dev)
99{
100 return;
101}
102
103static void pnp_set_resource(device_t dev, struct resource *resource)
104{
105 if (!(resource->flags & IORESOURCE_ASSIGNED)) {
Myles Watsonc4ddbff2009-02-09 17:52:54 +0000106 printk_err("ERROR: %s %02lx %s size: 0x%010Lx not assigned\n",
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000107 dev_path(dev), resource->index,
108 resource_type(resource),
109 resource->size);
Eric Biederman5cd81732004-03-11 15:01:31 +0000110 return;
111 }
Li-Ta Lo75337f72004-05-07 21:56:48 +0000112
Eric Biederman5cd81732004-03-11 15:01:31 +0000113 /* Now store the resource */
Eric Biederman5cd81732004-03-11 15:01:31 +0000114 if (resource->flags & IORESOURCE_IO) {
115 pnp_set_iobase(dev, resource->index, resource->base);
Eric Biedermanb78c1972004-10-14 20:54:17 +0000116 }
117 else if (resource->flags & IORESOURCE_DRQ) {
Eric Biederman5cd81732004-03-11 15:01:31 +0000118 pnp_set_drq(dev, resource->index, resource->base);
Eric Biedermanb78c1972004-10-14 20:54:17 +0000119 }
120 else if (resource->flags & IORESOURCE_IRQ) {
Eric Biederman5cd81732004-03-11 15:01:31 +0000121 pnp_set_irq(dev, resource->index, resource->base);
Eric Biedermanb78c1972004-10-14 20:54:17 +0000122 }
123 else {
Myles Watsonc4ddbff2009-02-09 17:52:54 +0000124 printk_err("ERROR: %s %02lx unknown resource type\n",
Eric Biedermanb78c1972004-10-14 20:54:17 +0000125 dev_path(dev), resource->index);
Eric Biederman5cd81732004-03-11 15:01:31 +0000126 return;
127 }
Li-Ta Lo9f0d0f92004-05-10 16:05:16 +0000128 resource->flags |= IORESOURCE_STORED;
Eric Biederman5cd81732004-03-11 15:01:31 +0000129
Eric Biederman03acab62004-10-14 21:25:53 +0000130 report_resource_stored(dev, resource, "");
Eric Biederman5cd81732004-03-11 15:01:31 +0000131}
132
133void pnp_set_resources(device_t dev)
134{
135 int i;
136
137 /* Select the device */
138 pnp_set_logical_device(dev);
139
140 /* Paranoia says I should disable the device here... */
Eric Biedermanb78c1972004-10-14 20:54:17 +0000141 for(i = 0; i < dev->resources; i++) {
Eric Biederman5cd81732004-03-11 15:01:31 +0000142 pnp_set_resource(dev, &dev->resource[i]);
143 }
144}
145
146void pnp_enable_resources(device_t dev)
147{
148 pnp_set_logical_device(dev);
149 pnp_set_enable(dev, 1);
Eric Biederman5cd81732004-03-11 15:01:31 +0000150}
151
152void pnp_enable(device_t dev)
153{
Li-Ta Lo75337f72004-05-07 21:56:48 +0000154 if (!dev->enabled) {
Li-Ta Loc6bcedb2004-04-21 16:57:05 +0000155 pnp_set_logical_device(dev);
Eric Biederman5cd81732004-03-11 15:01:31 +0000156 pnp_set_enable(dev, 0);
157 }
158}
159
160struct device_operations pnp_ops = {
161 .read_resources = pnp_read_resources,
162 .set_resources = pnp_set_resources,
163 .enable_resources = pnp_enable_resources,
164 .enable = pnp_enable,
165};
166
167/* PNP chip opertations */
168
Eric Biedermanb78c1972004-10-14 20:54:17 +0000169static void pnp_get_ioresource(device_t dev, unsigned index, struct io_info *info)
Eric Biederman5cd81732004-03-11 15:01:31 +0000170{
171 struct resource *resource;
Eric Biedermandbec2d42004-10-21 10:44:08 +0000172 unsigned moving, gran, step;
Li-Ta Lo75337f72004-05-07 21:56:48 +0000173
Eric Biederman03acab62004-10-14 21:25:53 +0000174 resource = new_resource(dev, index);
Eric Biederman5cd81732004-03-11 15:01:31 +0000175
176 /* Initilize the resource */
177 resource->limit = 0xffff;
178 resource->flags |= IORESOURCE_IO;
179
Eric Biedermandbec2d42004-10-21 10:44:08 +0000180 /* Get the resource size */
181 moving = info->mask;
182 gran = 15;
183 step = 1 << gran;
184 /* Find the first bit that moves */
185 while((moving & step) == 0) {
186 gran--;
187 step >>= 1;
188 }
189 /* Now find the first bit that does not move */
190 while((moving & step) != 0) {
191 gran--;
192 step >>= 1;
193 }
194 /* Of the moving bits the last bit in the first group,
195 * tells us the size of this resource.
196 */
197 if ((moving & step) == 0) {
198 gran++;
199 step <<= 1;
200 }
Eric Biederman5cd81732004-03-11 15:01:31 +0000201 /* Set the resource size and alignment */
Eric Biedermandbec2d42004-10-21 10:44:08 +0000202 resource->gran = gran;
203 resource->align = gran;
204 resource->limit = info->mask | (step - 1);
205 resource->size = 1 << gran;
Eric Biederman5cd81732004-03-11 15:01:31 +0000206}
207
208static void get_resources(device_t dev, struct pnp_info *info)
209{
210 struct resource *resource;
211
Eric Biederman5cd81732004-03-11 15:01:31 +0000212 if (info->flags & PNP_IO0) {
213 pnp_get_ioresource(dev, PNP_IDX_IO0, &info->io0);
214 }
215 if (info->flags & PNP_IO1) {
216 pnp_get_ioresource(dev, PNP_IDX_IO1, &info->io1);
217 }
Eric Biedermanb78c1972004-10-14 20:54:17 +0000218 if (info->flags & PNP_IO2) {
219 pnp_get_ioresource(dev, PNP_IDX_IO2, &info->io2);
220 }
221 if (info->flags & PNP_IO3) {
222 pnp_get_ioresource(dev, PNP_IDX_IO3, &info->io3);
223 }
Eric Biederman5cd81732004-03-11 15:01:31 +0000224 if (info->flags & PNP_IRQ0) {
Eric Biederman03acab62004-10-14 21:25:53 +0000225 resource = new_resource(dev, PNP_IDX_IRQ0);
Eric Biederman5cd81732004-03-11 15:01:31 +0000226 resource->size = 1;
227 resource->flags |= IORESOURCE_IRQ;
228 }
229 if (info->flags & PNP_IRQ1) {
Eric Biederman03acab62004-10-14 21:25:53 +0000230 resource = new_resource(dev, PNP_IDX_IRQ1);
Eric Biederman5cd81732004-03-11 15:01:31 +0000231 resource->size = 1;
232 resource->flags |= IORESOURCE_IRQ;
233 }
234 if (info->flags & PNP_DRQ0) {
Eric Biederman03acab62004-10-14 21:25:53 +0000235 resource = new_resource(dev, PNP_IDX_DRQ0);
Eric Biederman5cd81732004-03-11 15:01:31 +0000236 resource->size = 1;
237 resource->flags |= IORESOURCE_DRQ;
238 }
239 if (info->flags & PNP_DRQ1) {
Eric Biederman03acab62004-10-14 21:25:53 +0000240 resource = new_resource(dev, PNP_IDX_DRQ1);
Eric Biederman5cd81732004-03-11 15:01:31 +0000241 resource->size = 1;
242 resource->flags |= IORESOURCE_DRQ;
Stefan Reinauerde3206a2010-02-22 06:09:43 +0000243 }
244 /* These are not IRQs, but set the flag to have the
245 * resource allocator do the right thing
246 */
247 if (info->flags & PNP_EN) {
248 resource = new_resource(dev, PNP_IDX_EN);
249 resource->size = 1;
250 resource->flags |= IORESOURCE_IRQ;
251 }
252 if (info->flags & PNP_MSC0) {
253 resource = new_resource(dev, PNP_IDX_MSC0);
254 resource->size = 1;
255 resource->flags |= IORESOURCE_IRQ;
256 }
257 if (info->flags & PNP_MSC1) {
258 resource = new_resource(dev, PNP_IDX_MSC1);
259 resource->size = 1;
260 resource->flags |= IORESOURCE_IRQ;
261 }
Eric Biederman5cd81732004-03-11 15:01:31 +0000262}
263
Eric Biederman7003ba42004-10-16 06:20:29 +0000264void pnp_enable_devices(device_t base_dev, struct device_operations *ops,
265 unsigned functions, struct pnp_info *info)
Eric Biederman5cd81732004-03-11 15:01:31 +0000266{
267 struct device_path path;
268 device_t dev;
269 int i;
270
Eric Biederman5cd81732004-03-11 15:01:31 +0000271 path.type = DEVICE_PATH_PNP;
Stefan Reinauer2b34db82009-02-28 20:10:20 +0000272 path.pnp.port = base_dev->path.pnp.port;
Eric Biederman5cd81732004-03-11 15:01:31 +0000273
274 /* Setup the ops and resources on the newly allocated devices */
Eric Biedermanb78c1972004-10-14 20:54:17 +0000275 for(i = 0; i < functions; i++) {
Uwe Hermannaa4bedf2007-07-12 13:12:47 +0000276 /* Skip logical devices this Super I/O doesn't have. */
277 if (info[i].function == -1)
278 continue;
279
Stefan Reinauer2b34db82009-02-28 20:10:20 +0000280 path.pnp.device = info[i].function;
Eric Biederman7003ba42004-10-16 06:20:29 +0000281 dev = alloc_find_dev(base_dev->bus, &path);
282
283 /* Don't initialize a device multiple times */
284 if (dev->ops)
285 continue;
Li-Ta Loc6bcedb2004-04-21 16:57:05 +0000286
Li-Ta Lo75337f72004-05-07 21:56:48 +0000287 if (info[i].ops == 0) {
288 dev->ops = ops;
Eric Biedermanb78c1972004-10-14 20:54:17 +0000289 } else {
Li-Ta Lo75337f72004-05-07 21:56:48 +0000290 dev->ops = info[i].ops;
Li-Ta Loc6bcedb2004-04-21 16:57:05 +0000291 }
Eric Biederman5cd81732004-03-11 15:01:31 +0000292 get_resources(dev, &info[i]);
Eric Biederman5cd81732004-03-11 15:01:31 +0000293 }
294}