2007-04-22 21:08:13 +02:00
|
|
|
/*
|
2008-01-18 11:35:56 +01:00
|
|
|
* This file is part of the coreboot project.
|
2007-04-22 21:08:13 +02:00
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Linux Networx
|
|
|
|
* (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
|
|
|
|
* Copyright (C) 2004 Li-Ta Lo <ollie@lanl.gov>
|
|
|
|
* Copyright (C) 2005 Tyan
|
|
|
|
* (Written by Yinghai Lu <yhlu@tyan.com> for Tyan)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2013-02-23 18:37:27 +01:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2007-04-22 21:08:13 +02:00
|
|
|
*/
|
2004-03-11 16:01:31 +01:00
|
|
|
|
|
|
|
#include <console/console.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <arch/io.h>
|
|
|
|
#include <device/device.h>
|
|
|
|
#include <device/pnp.h>
|
|
|
|
|
|
|
|
/* PNP fundamental operations */
|
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
void pnp_write_config(device_t dev, u8 reg, u8 value)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
2009-02-28 21:10:20 +01:00
|
|
|
outb(reg, dev->path.pnp.port);
|
|
|
|
outb(value, dev->path.pnp.port + 1);
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
u8 pnp_read_config(device_t dev, u8 reg)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
2009-02-28 21:10:20 +01:00
|
|
|
outb(reg, dev->path.pnp.port);
|
|
|
|
return inb(dev->path.pnp.port + 1);
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pnp_set_logical_device(device_t dev)
|
|
|
|
{
|
2009-02-28 21:10:20 +01:00
|
|
|
pnp_write_config(dev, 0x07, dev->path.pnp.device & 0xff);
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pnp_set_enable(device_t dev, int enable)
|
|
|
|
{
|
2010-10-18 02:00:57 +02:00
|
|
|
u8 tmp, bitpos;
|
2008-02-18 21:32:46 +01:00
|
|
|
|
|
|
|
tmp = pnp_read_config(dev, 0x30);
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/* Handle virtual devices, which share the same LDN register. */
|
2009-02-28 21:10:20 +01:00
|
|
|
bitpos = (dev->path.pnp.device >> 8) & 0x7;
|
2008-02-18 21:32:46 +01:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
if (enable)
|
2008-02-18 21:32:46 +01:00
|
|
|
tmp |= (1 << bitpos);
|
2010-10-18 02:00:57 +02:00
|
|
|
else
|
2008-02-18 21:32:46 +01:00
|
|
|
tmp &= ~(1 << bitpos);
|
2010-10-18 02:00:57 +02:00
|
|
|
|
2008-02-18 21:32:46 +01:00
|
|
|
pnp_write_config(dev, 0x30, tmp);
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pnp_read_enable(device_t dev)
|
|
|
|
{
|
2010-10-18 02:00:57 +02:00
|
|
|
u8 tmp, bitpos;
|
|
|
|
|
2008-02-18 21:32:46 +01:00
|
|
|
tmp = pnp_read_config(dev, 0x30);
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/* Handle virtual devices, which share the same LDN register. */
|
2009-02-28 21:10:20 +01:00
|
|
|
bitpos = (dev->path.pnp.device >> 8) & 0x7;
|
2010-10-18 02:00:57 +02:00
|
|
|
|
2008-02-19 21:30:25 +01:00
|
|
|
return !!(tmp & (1 << bitpos));
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
void pnp_set_iobase(device_t dev, u8 index, u16 iobase)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Index == 0x60 or 0x62. */
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_write_config(dev, index + 0, (iobase >> 8) & 0xff);
|
|
|
|
pnp_write_config(dev, index + 1, iobase & 0xff);
|
|
|
|
}
|
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
void pnp_set_irq(device_t dev, u8 index, u8 irq)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Index == 0x70 or 0x72. */
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_write_config(dev, index, irq);
|
|
|
|
}
|
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
void pnp_set_drq(device_t dev, u8 index, u8 drq)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Index == 0x74. */
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_write_config(dev, index, drq & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PNP device operations */
|
|
|
|
|
|
|
|
void pnp_read_resources(device_t dev)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pnp_set_resource(device_t dev, struct resource *resource)
|
|
|
|
{
|
|
|
|
if (!(resource->flags & IORESOURCE_ASSIGNED)) {
|
2012-03-11 19:31:03 +01:00
|
|
|
printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx "
|
2010-10-18 02:00:57 +02:00
|
|
|
"not assigned\n", dev_path(dev), resource->index,
|
|
|
|
resource_type(resource), resource->size);
|
2004-03-11 16:01:31 +01:00
|
|
|
return;
|
|
|
|
}
|
2004-05-07 23:56:48 +02:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Now store the resource. */
|
2004-03-11 16:01:31 +01:00
|
|
|
if (resource->flags & IORESOURCE_IO) {
|
|
|
|
pnp_set_iobase(dev, resource->index, resource->base);
|
2010-10-18 02:00:57 +02:00
|
|
|
} else if (resource->flags & IORESOURCE_DRQ) {
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_set_drq(dev, resource->index, resource->base);
|
2010-10-18 02:00:57 +02:00
|
|
|
} else if (resource->flags & IORESOURCE_IRQ) {
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_set_irq(dev, resource->index, resource->base);
|
2010-10-18 02:00:57 +02:00
|
|
|
} else {
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
|
2010-10-18 02:00:57 +02:00
|
|
|
dev_path(dev), resource->index);
|
2004-03-11 16:01:31 +01:00
|
|
|
return;
|
|
|
|
}
|
2004-05-10 18:05:16 +02:00
|
|
|
resource->flags |= IORESOURCE_STORED;
|
2004-03-11 16:01:31 +01:00
|
|
|
|
2004-10-14 23:25:53 +02:00
|
|
|
report_resource_stored(dev, resource, "");
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pnp_set_resources(device_t dev)
|
|
|
|
{
|
2010-05-21 16:33:48 +02:00
|
|
|
struct resource *res;
|
2004-03-11 16:01:31 +01:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Select the logical device (LDN). */
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_set_logical_device(dev);
|
|
|
|
|
|
|
|
/* Paranoia says I should disable the device here... */
|
2010-10-18 02:00:57 +02:00
|
|
|
for (res = dev->resource_list; res; res = res->next)
|
2010-05-21 16:33:48 +02:00
|
|
|
pnp_set_resource(dev, res);
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void pnp_enable_resources(device_t dev)
|
|
|
|
{
|
|
|
|
pnp_set_logical_device(dev);
|
|
|
|
pnp_set_enable(dev, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pnp_enable(device_t dev)
|
|
|
|
{
|
2004-05-07 23:56:48 +02:00
|
|
|
if (!dev->enabled) {
|
2004-04-21 18:57:05 +02:00
|
|
|
pnp_set_logical_device(dev);
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_set_enable(dev, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
pnp: Provide alternative pnp_enable() implementation
The current default implementation of pnp_enable() only disables devices
- if set so in the devicetree - but does not enable them. Enablement takes
place in pnp_enable_resources(). Yet, many PnP chips implement their own
version of pnp_enable() which also enables devices if set in the devicetree.
It's arguable, if enabling those devices makes sense, before they get
resources assigned. Maybe we can't write the resource registers if not,
who knows? The least we can do is providing a common implementation for
this behavior, and get rid of some code duplication.
Used the following cocci:
@@
expression e;
@@
+pnp_alt_enable(e);
-pnp_set_logical_device(e);
(
-pnp_set_enable(e, !!e->enabled);
|
-(e->enabled) ? pnp_set_enable(e, 1) : pnp_set_enable(e, 0);
|
-if (e->enabled) { pnp_set_enable(e, 1); }
-else { pnp_set_enable(e, 0); }
)
Change-Id: I8d695e8fcd3cf8b847b1aa99326b51a554700bc4
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: http://review.coreboot.org/3480
Tested-by: build bot (Jenkins)
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-06-10 22:57:12 +02:00
|
|
|
void pnp_alt_enable(device_t dev)
|
|
|
|
{
|
|
|
|
pnp_set_logical_device(dev);
|
|
|
|
pnp_set_enable(dev, !!dev->enabled);
|
|
|
|
}
|
|
|
|
|
2004-03-11 16:01:31 +01:00
|
|
|
struct device_operations pnp_ops = {
|
|
|
|
.read_resources = pnp_read_resources,
|
|
|
|
.set_resources = pnp_set_resources,
|
|
|
|
.enable_resources = pnp_enable_resources,
|
|
|
|
.enable = pnp_enable,
|
|
|
|
};
|
|
|
|
|
2010-11-05 00:23:47 +01:00
|
|
|
/* PNP chip operations */
|
2004-03-11 16:01:31 +01:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
static void pnp_get_ioresource(device_t dev, u8 index, struct io_info *info)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
|
|
|
struct resource *resource;
|
2004-10-21 12:44:08 +02:00
|
|
|
unsigned moving, gran, step;
|
2004-05-07 23:56:48 +02:00
|
|
|
|
2011-01-31 22:16:48 +01:00
|
|
|
if (!info->mask) {
|
|
|
|
printk(BIOS_ERR, "ERROR: device %s index %d has no mask.\n",
|
|
|
|
dev_path(dev), index);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-10-14 23:25:53 +02:00
|
|
|
resource = new_resource(dev, index);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Initilize the resource. */
|
2004-03-11 16:01:31 +01:00
|
|
|
resource->limit = 0xffff;
|
|
|
|
resource->flags |= IORESOURCE_IO;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Get the resource size... */
|
|
|
|
|
2004-10-21 12:44:08 +02:00
|
|
|
moving = info->mask;
|
|
|
|
gran = 15;
|
|
|
|
step = 1 << gran;
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/* Find the first bit that moves. */
|
|
|
|
while ((moving & step) == 0) {
|
2004-10-21 12:44:08 +02:00
|
|
|
gran--;
|
|
|
|
step >>= 1;
|
|
|
|
}
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/* Now find the first bit that does not move. */
|
|
|
|
while ((moving & step) != 0) {
|
2004-10-21 12:44:08 +02:00
|
|
|
gran--;
|
|
|
|
step >>= 1;
|
|
|
|
}
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Of the moving bits the last bit in the first group,
|
2004-10-21 12:44:08 +02:00
|
|
|
* tells us the size of this resource.
|
|
|
|
*/
|
|
|
|
if ((moving & step) == 0) {
|
|
|
|
gran++;
|
|
|
|
step <<= 1;
|
|
|
|
}
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/* Set the resource size and alignment. */
|
2004-10-21 12:44:08 +02:00
|
|
|
resource->gran = gran;
|
|
|
|
resource->align = gran;
|
|
|
|
resource->limit = info->mask | (step - 1);
|
|
|
|
resource->size = 1 << gran;
|
2004-03-11 16:01:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_resources(device_t dev, struct pnp_info *info)
|
|
|
|
{
|
|
|
|
struct resource *resource;
|
|
|
|
|
2010-11-05 00:23:47 +01:00
|
|
|
if (info->flags & PNP_IO0)
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_get_ioresource(dev, PNP_IDX_IO0, &info->io0);
|
2010-11-05 00:23:47 +01:00
|
|
|
if (info->flags & PNP_IO1)
|
2004-03-11 16:01:31 +01:00
|
|
|
pnp_get_ioresource(dev, PNP_IDX_IO1, &info->io1);
|
2010-11-05 00:23:47 +01:00
|
|
|
if (info->flags & PNP_IO2)
|
2004-10-14 22:54:17 +02:00
|
|
|
pnp_get_ioresource(dev, PNP_IDX_IO2, &info->io2);
|
2010-11-05 00:23:47 +01:00
|
|
|
if (info->flags & PNP_IO3)
|
2004-10-14 22:54:17 +02:00
|
|
|
pnp_get_ioresource(dev, PNP_IDX_IO3, &info->io3);
|
2010-11-05 00:23:47 +01:00
|
|
|
|
2004-03-11 16:01:31 +01:00
|
|
|
if (info->flags & PNP_IRQ0) {
|
2004-10-14 23:25:53 +02:00
|
|
|
resource = new_resource(dev, PNP_IDX_IRQ0);
|
2004-03-11 16:01:31 +01:00
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_IRQ;
|
|
|
|
}
|
|
|
|
if (info->flags & PNP_IRQ1) {
|
2004-10-14 23:25:53 +02:00
|
|
|
resource = new_resource(dev, PNP_IDX_IRQ1);
|
2004-03-11 16:01:31 +01:00
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_IRQ;
|
|
|
|
}
|
2010-11-05 00:23:47 +01:00
|
|
|
|
2004-03-11 16:01:31 +01:00
|
|
|
if (info->flags & PNP_DRQ0) {
|
2004-10-14 23:25:53 +02:00
|
|
|
resource = new_resource(dev, PNP_IDX_DRQ0);
|
2004-03-11 16:01:31 +01:00
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_DRQ;
|
|
|
|
}
|
|
|
|
if (info->flags & PNP_DRQ1) {
|
2004-10-14 23:25:53 +02:00
|
|
|
resource = new_resource(dev, PNP_IDX_DRQ1);
|
2004-03-11 16:01:31 +01:00
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_DRQ;
|
2010-02-22 07:09:43 +01:00
|
|
|
}
|
2010-10-18 02:00:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* These are not IRQs, but set the flag to have the
|
|
|
|
* resource allocator do the right thing.
|
2010-02-22 07:09:43 +01:00
|
|
|
*/
|
|
|
|
if (info->flags & PNP_EN) {
|
|
|
|
resource = new_resource(dev, PNP_IDX_EN);
|
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_IRQ;
|
|
|
|
}
|
|
|
|
if (info->flags & PNP_MSC0) {
|
|
|
|
resource = new_resource(dev, PNP_IDX_MSC0);
|
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_IRQ;
|
|
|
|
}
|
|
|
|
if (info->flags & PNP_MSC1) {
|
|
|
|
resource = new_resource(dev, PNP_IDX_MSC1);
|
|
|
|
resource->size = 1;
|
|
|
|
resource->flags |= IORESOURCE_IRQ;
|
|
|
|
}
|
2010-04-27 08:56:47 +02:00
|
|
|
}
|
2004-03-11 16:01:31 +01:00
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
void pnp_enable_devices(device_t base_dev, struct device_operations *ops,
|
2010-10-18 02:00:57 +02:00
|
|
|
unsigned int functions, struct pnp_info *info)
|
2004-03-11 16:01:31 +01:00
|
|
|
{
|
|
|
|
struct device_path path;
|
|
|
|
device_t dev;
|
|
|
|
int i;
|
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
path.type = DEVICE_PATH_PNP;
|
2009-02-28 21:10:20 +01:00
|
|
|
path.pnp.port = base_dev->path.pnp.port;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Setup the ops and resources on the newly allocated devices. */
|
|
|
|
for (i = 0; i < functions; i++) {
|
2007-07-12 15:12:47 +02:00
|
|
|
/* Skip logical devices this Super I/O doesn't have. */
|
|
|
|
if (info[i].function == -1)
|
|
|
|
continue;
|
|
|
|
|
2009-02-28 21:10:20 +01:00
|
|
|
path.pnp.device = info[i].function;
|
2004-10-16 08:20:29 +02:00
|
|
|
dev = alloc_find_dev(base_dev->bus, &path);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
/* Don't initialize a device multiple times. */
|
2010-04-27 08:56:47 +02:00
|
|
|
if (dev->ops)
|
2004-10-16 08:20:29 +02:00
|
|
|
continue;
|
2004-04-21 18:57:05 +02:00
|
|
|
|
2010-10-18 02:00:57 +02:00
|
|
|
if (info[i].ops == 0)
|
2004-05-07 23:56:48 +02:00
|
|
|
dev->ops = ops;
|
2010-10-18 02:00:57 +02:00
|
|
|
else
|
2004-05-07 23:56:48 +02:00
|
|
|
dev->ops = info[i].ops;
|
2010-10-18 02:00:57 +02:00
|
|
|
|
2004-03-11 16:01:31 +01:00
|
|
|
get_resources(dev, &info[i]);
|
|
|
|
}
|
|
|
|
}
|