Commit c907be95 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] PPC32: New OCP core support (updated)

From: Matt Porter <mporter@kernel.crashing.org>

New OCP infrastructure ported from 2.4 along with several enhancements. 
Updated patch with comments from hch and Valdis.
parent 120cf52f
......@@ -1238,7 +1238,7 @@ config SERIAL_TEXT_DEBUG
bool "Support for early boot texts over serial port"
depends on 4xx || GT64260 || LOPEC || PPLUS || PRPMC800 || PPC_GEN550
config OCP
config PPC_OCP
bool
depends on IBM_OCP
default y
......
......@@ -43,7 +43,6 @@ core-$(CONFIG_APUS) += arch/ppc/amiga/
drivers-$(CONFIG_8xx) += arch/ppc/8xx_io/
drivers-$(CONFIG_4xx) += arch/ppc/4xx_io/
drivers-$(CONFIG_8260) += arch/ppc/8260_io/
drivers-$(CONFIG_OCP) += arch/ppc/ocp/
BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm
......
......@@ -37,6 +37,7 @@
#include <asm/sections.h>
#include <asm/nvram.h>
#include <asm/xmon.h>
#include <asm/ocp.h>
#if defined CONFIG_KGDB
#include <asm/kgdb.h>
......@@ -683,6 +684,12 @@ void __init setup_arch(char **cmdline_p)
do_init_bootmem();
if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab);
#ifdef CONFIG_PPC_OCP
/* Initialize OCP device list */
ocp_early_init();
if ( ppc_md.progress ) ppc_md.progress("ocp: exit", 0x3eab);
#endif
ppc_md.setup_arch();
if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab);
......
#
# Makefile for the linux kernel.
#
obj-y := ocp.o ocp-driver.o ocp-probe.o
/*
* FILE NAME: ocp-driver.c
*
* BRIEF MODULE DESCRIPTION:
* driver callback, id matching and registration
* Based on drivers/pci/pci-driver, Copyright (c) 1997--1999 Martin Mares
*
* Maintained by: Armin <akuster@mvista.com>
*
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/ocp.h>
#include <linux/module.h>
#include <linux/init.h>
/*
* Registration of OCP drivers and handling of hot-pluggable devices.
*/
static int
ocp_device_probe(struct device *dev)
{
int error = 0;
struct ocp_driver *drv;
struct ocp_device *ocp_dev;
drv = to_ocp_driver(dev->driver);
ocp_dev = to_ocp_dev(dev);
if (drv->probe) {
error = drv->probe(ocp_dev);
DBG("probe return code %d\n", error);
if (error >= 0) {
ocp_dev->driver = drv;
error = 0;
}
}
return error;
}
static int
ocp_device_remove(struct device *dev)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
if (ocp_dev->driver) {
if (ocp_dev->driver->remove)
ocp_dev->driver->remove(ocp_dev);
ocp_dev->driver = NULL;
}
return 0;
}
static int
ocp_device_suspend(struct device *dev, u32 state, u32 level)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
int error = 0;
if (ocp_dev->driver) {
if (level == SUSPEND_SAVE_STATE && ocp_dev->driver->save_state)
error = ocp_dev->driver->save_state(ocp_dev, state);
else if (level == SUSPEND_POWER_DOWN
&& ocp_dev->driver->suspend)
error = ocp_dev->driver->suspend(ocp_dev, state);
}
return error;
}
static int
ocp_device_resume(struct device *dev, u32 level)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
if (ocp_dev->driver) {
if (level == RESUME_POWER_ON && ocp_dev->driver->resume)
ocp_dev->driver->resume(ocp_dev);
}
return 0;
}
/**
* ocp_bus_match - Works out whether an OCP device matches any
* of the IDs listed for a given OCP driver.
* @dev: the generic device struct for the OCP device
* @drv: the generic driver struct for the OCP driver
*
* Used by a driver to check whether a OCP device present in the
* system is in its list of supported devices. Returns 1 for a
* match, or 0 if there is no match.
*/
static int
ocp_bus_match(struct device *dev, struct device_driver *drv)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_driver(drv);
const struct ocp_device_id *ids = ocp_drv->id_table;
if (!ids)
return 0;
while (ids->vendor || ids->device) {
if ((ids->vendor == OCP_ANY_ID
|| ids->vendor == ocp_dev->vendor)
&& (ids->device == OCP_ANY_ID
|| ids->device == ocp_dev->device)) {
DBG("Bus match -vendor:%x device:%x\n", ids->vendor,
ids->device);
return 1;
}
ids++;
}
return 0;
}
struct bus_type ocp_bus_type = {
.name = "ocp",
.match = ocp_bus_match,
};
static int __init
ocp_driver_init(void)
{
return bus_register(&ocp_bus_type);
}
postcore_initcall(ocp_driver_init);
/**
* ocp_register_driver - register a new ocp driver
* @drv: the driver structure to register
*
* Adds the driver structure to the list of registered drivers
* Returns the number of ocp devices which were claimed by the driver
* during registration. The driver remains registered even if the
* return value is zero.
*/
int
ocp_register_driver(struct ocp_driver *drv)
{
int count = 0;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &ocp_bus_type;
drv->driver.probe = ocp_device_probe;
drv->driver.resume = ocp_device_resume;
drv->driver.suspend = ocp_device_suspend;
drv->driver.remove = ocp_device_remove;
/* register with core */
count = driver_register(&drv->driver);
return count ? count : 1;
}
/**
* ocp_unregister_driver - unregister a ocp driver
* @drv: the driver structure to unregister
*
* Deletes the driver structure from the list of registered OCP drivers,
* gives it a chance to clean up by calling its remove() function for
* each device it was responsible for, and marks those devices as
* driverless.
*/
void
ocp_unregister_driver(struct ocp_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL(ocp_register_driver);
EXPORT_SYMBOL(ocp_unregister_driver);
EXPORT_SYMBOL(ocp_bus_type);
/*
* FILE NAME: ocp-probe.c
*
* BRIEF MODULE DESCRIPTION:
* Device scanning & bus set routines
* Based on drivers/pci/probe, Copyright (c) 1997--1999 Martin Mares
*
* Maintained by: Armin <akuster@mvista.com>
*
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/ocp.h>
LIST_HEAD(ocp_devices);
struct device *ocp_bus;
static struct ocp_device * __devinit
ocp_setup_dev(struct ocp_def *odef, unsigned int index)
{
struct ocp_device *dev;
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, sizeof(*dev));
dev->vendor = odef->vendor;
dev->device = odef->device;
dev->num = ocp_get_num(dev->device);
dev->paddr = odef->paddr;
dev->irq = odef->irq;
dev->pm = odef->pm;
dev->current_state = 4;
sprintf(dev->name, "OCP device %04x:%04x", dev->vendor, dev->device);
DBG("%s %s 0x%lx irq:%d pm:0x%lx \n", dev->slot_name, dev->name,
(unsigned long) dev->paddr, dev->irq, dev->pm);
/* now put in global tree */
sprintf(dev->dev.bus_id, "%d", index);
dev->dev.parent = ocp_bus;
dev->dev.bus = &ocp_bus_type;
device_register(&dev->dev);
return dev;
}
static struct device * __devinit ocp_alloc_primary_bus(void)
{
struct device *b;
b = kmalloc(sizeof(struct device), GFP_KERNEL);
if (b == NULL)
return NULL;
memset(b, 0, sizeof(struct device));
strcpy(b->bus_id, "ocp");
device_register(b);
return b;
}
void __devinit ocp_setup_devices(struct ocp_def *odef)
{
int index;
struct ocp_device *dev;
if (ocp_bus == NULL)
ocp_bus = ocp_alloc_primary_bus();
for (index = 0; odef->vendor != OCP_VENDOR_INVALID; ++index, ++odef) {
dev = ocp_setup_dev(odef, index);
if (dev != NULL)
list_add_tail(&dev->global_list, &ocp_devices);
}
}
extern struct ocp_def core_ocp[];
static int __init
ocparch_init(void)
{
ocp_setup_devices(core_ocp);
return 0;
}
subsys_initcall(ocparch_init);
EXPORT_SYMBOL(ocp_devices);
/*
* ocp.c
*
* The is drived from pci.c
*
* Current Maintainer
* Armin Kuster akuster@dslextreme.com
* Jan, 2002
*
*
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/list.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/ocp.h>
#include <asm/errno.h>
/**
* ocp_get_num - This determines how many OCP devices of a given
* device are registered
* @device: OCP device such as HOST, PCI, GPT, UART, OPB, IIC, GPIO, EMAC, ZMII,
*
* The routine returns the number that devices which is registered
*/
unsigned int ocp_get_num(unsigned int device)
{
unsigned int count = 0;
struct ocp_device *ocp;
struct list_head *ocp_l;
list_for_each(ocp_l, &ocp_devices) {
ocp = list_entry(ocp_l, struct ocp_device, global_list);
if (device == ocp->device)
count++;
}
return count;
}
/**
* ocp_get_dev - get ocp driver pointer for ocp device and instance of it
* @device: OCP device such as PCI, GPT, UART, OPB, IIC, GPIO, EMAC, ZMII
* @dev_num: ocp device number whos paddr you want
*
* The routine returns ocp device pointer
* in list based on device and instance of that device
*
*/
struct ocp_device *
ocp_get_dev(unsigned int device, int dev_num)
{
struct ocp_device *ocp;
struct list_head *ocp_l;
int count = 0;
list_for_each(ocp_l, &ocp_devices) {
ocp = list_entry(ocp_l, struct ocp_device, global_list);
if (device == ocp->device) {
if (dev_num == count)
return ocp;
count++;
}
}
return NULL;
}
EXPORT_SYMBOL(ocp_get_dev);
EXPORT_SYMBOL(ocp_get_num);
#ifdef CONFIG_PM
int ocp_generic_suspend(struct ocp_device *pdev, u32 state)
{
ocp_force_power_off(pdev);
return 0;
}
int ocp_generic_resume(struct ocp_device *pdev)
{
ocp_force_power_on(pdev);
}
EXPORT_SYMBOL(ocp_generic_suspend);
EXPORT_SYMBOL(ocp_generic_resume);
#endif /* CONFIG_PM */
......@@ -13,6 +13,7 @@ CFLAGS_prom_init.o += -mrelocatable-lib
CFLAGS_btext.o += -mrelocatable-lib
obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o
obj-$(CONFIG_PPC_OCP) += ocp.o
obj-$(CONFIG_44x) += ibm44x_common.o
obj-$(CONFIG_440GP) += ibm440gp_common.o
ifeq ($(CONFIG_4xx),y)
......
/*
* ocp.c
*
* (c) Benjamin Herrenschmidt (benh@kernel.crashing.org)
* Mipsys - France
*
* Derived from work (c) Armin Kuster akuster@pacbell.net
*
* Additional support and port to 2.6 LDM/sysfs by
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2004 MontaVista Software, Inc.
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
* OCP (On Chip Peripheral) is a software emulated "bus" with a
* pseudo discovery method for dumb peripherals. Usually these type
* of peripherals are found on embedded SoC (System On a Chip)
* processors or highly integrated system controllers that have
* a host bridge and many peripherals. Common examples where
* this is already used include the PPC4xx, PPC85xx, MPC52xx,
* and MV64xxx parts.
*
* This subsystem creates a standard OCP bus type within the
* device model. The devices on the OCP bus are seeded by an
* an initial OCP device array created by the arch-specific
* Device entries can be added/removed/modified through OCP
* helper functions to accomodate system and board-specific
* parameters commonly found in embedded systems. OCP also
* provides a standard method for devices to describe extended
* attributes about themselves to the system. A standard access
* method allows OCP drivers to obtain the information, both
* SoC-specific and system/board-specific, needed for operation.
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/bootmem.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/ocp.h>
#include <asm/errno.h>
#include <asm/rwsem.h>
#include <asm/semaphore.h>
//#define DBG(x) printk x
#define DBG(x)
extern int mem_init_done;
extern struct ocp_def core_ocp[]; /* Static list of devices, provided by
CPU core */
LIST_HEAD(ocp_devices); /* List of all OCP devices */
DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */
static int ocp_inited;
/* Sysfs support */
#define OCP_DEF_ATTR(field, format_string) \
static ssize_t \
show_##field(struct device *dev, char *buf) \
{ \
struct ocp_device *odev = to_ocp_dev(dev); \
\
return sprintf(buf, format_string, odev->def->field); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
OCP_DEF_ATTR(vendor, "0x%04x\n");
OCP_DEF_ATTR(function, "0x%04x\n");
OCP_DEF_ATTR(index, "0x%04x\n");
#ifdef CONFIG_PTE_64BIT
OCP_DEF_ATTR(paddr, "0x%16Lx\n");
#else
OCP_DEF_ATTR(paddr, "0x%08lx\n");
#endif
OCP_DEF_ATTR(irq, "%d\n");
OCP_DEF_ATTR(pm, "%lu\n");
void ocp_create_sysfs_dev_files(struct ocp_device *odev)
{
struct device *dev = &odev->dev;
/* Current OCP device def attributes */
device_create_file(dev, &dev_attr_vendor);
device_create_file(dev, &dev_attr_function);
device_create_file(dev, &dev_attr_index);
device_create_file(dev, &dev_attr_paddr);
device_create_file(dev, &dev_attr_irq);
device_create_file(dev, &dev_attr_pm);
/* Current OCP device additions attributes */
if (odev->def->additions && odev->def->show)
odev->def->show(dev);
}
/**
* ocp_device_match - Match one driver to one device
* @drv: driver to match
* @dev: device to match
*
* This function returns 0 if the driver and device don't match
*/
static int
ocp_device_match(struct device *dev, struct device_driver *drv)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_drv(drv);
const struct ocp_device_id *ids = ocp_drv->id_table;
if (!ids)
return 0;
while (ids->vendor || ids->function) {
if ((ids->vendor == OCP_ANY_ID
|| ids->vendor == ocp_dev->def->vendor)
&& (ids->function == OCP_ANY_ID
|| ids->function == ocp_dev->def->function))
return 1;
ids++;
}
return 0;
}
static int
ocp_device_probe(struct device *dev)
{
int error = 0;
struct ocp_driver *drv;
struct ocp_device *ocp_dev;
drv = to_ocp_drv(dev->driver);
ocp_dev = to_ocp_dev(dev);
if (drv->probe) {
error = drv->probe(ocp_dev);
if (error >= 0) {
ocp_dev->driver = drv;
error = 0;
}
}
return error;
}
static int
ocp_device_remove(struct device *dev)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
if (ocp_dev->driver) {
if (ocp_dev->driver->remove)
ocp_dev->driver->remove(ocp_dev);
ocp_dev->driver = NULL;
}
return 0;
}
static int
ocp_device_suspend(struct device *dev, u32 state)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver);
if (dev->driver && ocp_drv->suspend)
return ocp_drv->suspend(ocp_dev, state);
return 0;
}
static int
ocp_device_resume(struct device *dev)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver);
if (dev->driver && ocp_drv->resume)
return ocp_drv->resume(ocp_dev);
return 0;
}
struct bus_type ocp_bus_type = {
.name = "ocp",
.match = ocp_device_match,
.suspend = ocp_device_suspend,
.resume = ocp_device_resume,
};
/**
* ocp_register_driver - Register an OCP driver
* @drv: pointer to statically defined ocp_driver structure
*
* The driver's probe() callback is called either recursively
* by this function or upon later call of ocp_driver_init
*
* NOTE: Detection of devices is a 2 pass step on this implementation,
* hotswap isn't supported. First, all OCP devices are put in the device
* list, _then_ all drivers are probed on each match.
*
* This function returns a count of how many devices actually matched
* (whether the probe routine returned 0 or -ENODEV, a different error
* code isn't considered as a match).
*/
int
ocp_register_driver(struct ocp_driver *drv)
{
int count = 0;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &ocp_bus_type;
drv->driver.probe = ocp_device_probe;
drv->driver.remove = ocp_device_remove;
/* register with core */
count = driver_register(&drv->driver);
return count ? count : 1;
}
/**
* ocp_unregister_driver - Unregister an OCP driver
* @drv: pointer to statically defined ocp_driver structure
*
* The driver's remove() callback is called recursively
* by this function for any device already registered
*/
void
ocp_unregister_driver(struct ocp_driver *drv)
{
DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name));
driver_unregister(&drv->driver);
DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name));
}
/* Core of ocp_find_device(). Caller must hold ocp_devices_sem */
static struct ocp_device *
__ocp_find_device(unsigned int vendor, unsigned int function, int index)
{
struct list_head *entry;
struct ocp_device *dev, *found = NULL;
DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index));
list_for_each(entry, &ocp_devices) {
dev = list_entry(entry, struct ocp_device, link);
if (vendor != OCP_ANY_ID && vendor != dev->def->vendor)
continue;
if (function != OCP_ANY_ID && function != dev->def->function)
continue;
if (index != OCP_ANY_INDEX && index != dev->def->index)
continue;
found = dev;
break;
}
DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index));
return found;
}
/**
* ocp_find_device - Find a device by function & index
* @vendor: vendor ID of the device (or OCP_ANY_ID)
* @function: function code of the device (or OCP_ANY_ID)
* @idx: index of the device (or OCP_ANY_INDEX)
*
* This function allows a lookup of a given function by it's
* index, it's typically used to find the MAL or ZMII associated
* with an EMAC or similar horrors.
* You can pass vendor, though you usually want OCP_ANY_ID there...
*/
struct ocp_device *
ocp_find_device(unsigned int vendor, unsigned int function, int index)
{
struct ocp_device *dev;
down_read(&ocp_devices_sem);
dev = __ocp_find_device(vendor, function, index);
up_read(&ocp_devices_sem);
return dev;
}
/**
* ocp_get_one_device - Find a def by function & index
* @vendor: vendor ID of the device (or OCP_ANY_ID)
* @function: function code of the device (or OCP_ANY_ID)
* @idx: index of the device (or OCP_ANY_INDEX)
*
* This function allows a lookup of a given ocp_def by it's
* vendor, function, and index. The main purpose for is to
* allow modification of the def before binding to the driver
*/
struct ocp_def *
ocp_get_one_device(unsigned int vendor, unsigned int function, int index)
{
struct ocp_device *dev;
struct ocp_def *found = NULL;
DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n",
vendor, function, index));
dev = ocp_find_device(vendor, function, index);
if (dev)
found = dev->def;
DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n",
vendor, function, index));
return found;
}
/**
* ocp_add_one_device - Add a device
* @def: static device definition structure
*
* This function adds a device definition to the
* device list. It may only be called before
* ocp_driver_init() and will return an error
* otherwise.
*/
int
ocp_add_one_device(struct ocp_def *def)
{
struct ocp_device *dev;
DBG(("ocp: ocp_add_one_device()...\n"));
/* Can't be called after ocp driver init */
if (ocp_inited)
return 1;
if (mem_init_done)
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
else
dev = alloc_bootmem(sizeof(*dev));
if (dev == NULL)
return 1;
memset(dev, 0, sizeof(*dev));
dev->def = def;
dev->current_state = 4;
sprintf(dev->name, "OCP device %04x:%04x:%04x",
dev->def->vendor, dev->def->function, dev->def->index);
down_write(&ocp_devices_sem);
list_add_tail(&dev->link, &ocp_devices);
up_write(&ocp_devices_sem);
DBG(("ocp: ocp_add_one_device()...done\n"));
return 0;
}
/**
* ocp_remove_one_device - Remove a device by function & index
* @vendor: vendor ID of the device (or OCP_ANY_ID)
* @function: function code of the device (or OCP_ANY_ID)
* @idx: index of the device (or OCP_ANY_INDEX)
*
* This function allows removal of a given function by its
* index. It may only be called before ocp_driver_init()
* and will return an error otherwise.
*/
int
ocp_remove_one_device(unsigned int vendor, unsigned int function, int index)
{
struct ocp_device *dev;
DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index));
/* Can't be called after ocp driver init */
if (ocp_inited)
return 1;
down_write(&ocp_devices_sem);
dev = __ocp_find_device(vendor, function, index);
list_del((struct list_head *)dev);
up_write(&ocp_devices_sem);
DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index));
return 0;
}
/**
* ocp_for_each_device - Iterate over OCP devices
* @callback: routine to execute for each ocp device.
* @arg: user data to be passed to callback routine.
*
* This routine holds the ocp_device semaphore, so the
* callback routine cannot modify the ocp_device list.
*/
void
ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg)
{
struct list_head *entry;
if (callback) {
down_read(&ocp_devices_sem);
list_for_each(entry, &ocp_devices)
callback(list_entry(entry, struct ocp_device, link),
arg);
up_read(&ocp_devices_sem);
}
}
/**
* ocp_early_init - Init OCP device management
*
* This function builds the list of devices before setup_arch.
* This allows platform code to modify the device lists before
* they are bound to drivers (changes to paddr, removing devices
* etc)
*/
int __init
ocp_early_init(void)
{
struct ocp_def *def;
DBG(("ocp: ocp_early_init()...\n"));
/* Fill the devices list */
for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++)
ocp_add_one_device(def);
DBG(("ocp: ocp_early_init()... done.\n"));
return 0;
}
/**
* ocp_driver_init - Init OCP device management
*
* This function is meant to be called via OCP bus registration.
*/
static int __init
ocp_driver_init(void)
{
int ret = 0, index = 0;
struct device *ocp_bus;
struct list_head *entry;
struct ocp_device *dev;
if (ocp_inited)
return ret;
ocp_inited = 1;
DBG(("ocp: ocp_driver_init()...\n"));
/* Allocate/register primary OCP bus */
ocp_bus = kmalloc(sizeof(struct device), GFP_KERNEL);
if (ocp_bus == NULL)
return 1;
memset(ocp_bus, 0, sizeof(struct device));
strcpy(ocp_bus->bus_id, "ocp");
bus_register(&ocp_bus_type);
device_register(ocp_bus);
/* Put each OCP device into global device list */
list_for_each(entry, &ocp_devices) {
dev = list_entry(entry, struct ocp_device, link);
sprintf(dev->dev.bus_id, "%2.2x", index);
dev->dev.parent = ocp_bus;
dev->dev.bus = &ocp_bus_type;
device_register(&dev->dev);
ocp_create_sysfs_dev_files(dev);
index++;
}
DBG(("ocp: ocp_driver_init()... done.\n"));
return 0;
}
postcore_initcall(ocp_driver_init);
EXPORT_SYMBOL(ocp_bus_type);
EXPORT_SYMBOL(ocp_find_device);
EXPORT_SYMBOL(ocp_register_driver);
EXPORT_SYMBOL(ocp_unregister_driver);
/*
* ocp.h
*
* (c) Benjamin Herrenschmidt (benh@kernel.crashing.org)
* Mipsys - France
*
* Current Maintainer
* Armin Kuster akuster@pacbell.net
* Jan, 2002
*
* Derived from work (c) Armin Kuster akuster@pacbell.net
*
* Additional support and port to 2.6 LDM/sysfs by
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2003-2004 MontaVista Software, Inc.
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
* TODO: - Add get/put interface & fixup locking to provide same API for
* 2.4 and 2.5
* - Rework PM callbacks
*/
#ifdef __KERNEL__
#ifndef __OCP_H__
#define __OCP_H__
#include <linux/init.h>
#include <linux/list.h>
#include <linux/config.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <asm/mmu.h>
#include <asm/ocp_ids.h>
#include <asm/mmu.h> /* For phys_addr_t */
#undef DEBUG
/* #define DEBUG*/
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#include <asm/rwsem.h>
#include <asm/semaphore.h>
#define OCP_MAX_IRQS 7
#define MAX_EMACS 4
#define OCP_IRQ_NA -1 /* used when ocp device does not have an irq */
#define OCP_IRQ_MUL -2 /* used for ocp devices with multiple irqs */
#define OCP_NULL_TYPE 0 /* used to mark end of list */
#define OCP_DEV_NA -1
#define OCP_IRQ_MUL -2 /* used for ocp devices with multiply irqs */
#define OCP_NULL_TYPE -1 /* used to mark end of list */
#define OCP_CPM_NA 0 /* No Clock or Power Management avaliable */
#define OCP_PADDR_NA 0 /* No MMIO registers */
#define OCP_ANY_ID (~0)
#define OCP_ANY_INDEX -1
extern struct list_head ocp_root_buses;
extern struct list_head ocp_devices;
extern struct list_head ocp_devices;
extern struct rw_semaphore ocp_devices_sem;
struct ocp_device_id {
unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */
char name[16];
char desc[50];
unsigned long driver_data; /* Data private to the driver */
unsigned int vendor, function; /* Vendor and function ID or OCP_ANY_ID */
unsigned long driver_data; /* Data private to the driver */
};
struct func_info {
char name[16];
char desc[50];
};
/*
* Static definition of an OCP device.
*
* @vendor: Vendor code. It is _STRONGLY_ discouraged to use
* the vendor code as a way to match a unique device,
* though I kept that possibility open, you should
* really define different function codes for different
* device types
* @function: This is the function code for this device.
* @index: This index is used for mapping the Nth function of a
* given core. This is typically used for cross-driver
* matching, like looking for a given MAL or ZMII from
* an EMAC or for getting to the proper set of DCRs.
* Indices are no longer magically calculated based on
* structure ordering, they have to be actually coded
* into the ocp_def to avoid any possible confusion
* I _STRONGLY_ (again ? wow !) encourage anybody relying
* on index mapping to encode the "target" index in an
* associated structure pointed to by "additions", see
* how it's done for the EMAC driver.
* @paddr: Device physical address (may not mean anything...)
* @irq: Interrupt line for this device (TODO: think about making
* an array with this)
* @pm: Currently, contains the bitmask in CPMFR DCR for the device
* @additions: Optionally points to a function specific structure
* providing additional informations for a given device
* instance. It's currently used by the EMAC driver for MAL
* channel & ZMII port mapping among others.
* @show: Optionally points to a function specific structure
* providing a sysfs show routine for additions fields.
*/
struct ocp_def {
unsigned int vendor;
unsigned int device;
phys_addr_t paddr;
int irq;
unsigned long pm;
unsigned int vendor;
unsigned int function;
int index;
phys_addr_t paddr;
int irq;
unsigned long pm;
void *additions;
void (*show)(struct device *);
};
/* Struct for single ocp device managment */
/* Struct for a given device instance */
struct ocp_device {
struct list_head global_list;
unsigned int num; /* instance of device */
char name[80]; /* device name */
unsigned int vendor;
unsigned int device;
phys_addr_t paddr;
int irq;
unsigned long pm;
void *ocpdev; /* driver data for this device */
struct ocp_driver *driver;
u32 current_state; /* Current operating state. In ACPI-speak,
this is D0-D3, D0 being fully functional,
and D3 being off. */
struct device dev;
struct list_head link;
char name[80]; /* device name */
struct ocp_def *def; /* device definition */
void *drvdata; /* driver data for this device */
struct ocp_driver *driver;
u32 current_state; /* Current operating state. In ACPI-speak,
this is D0-D3, D0 being fully functional,
and D3 being off. */
struct device dev;
};
struct ocp_driver {
......@@ -106,23 +117,13 @@ struct ocp_driver {
const struct ocp_device_id *id_table; /* NULL if wants all devices */
int (*probe) (struct ocp_device *dev); /* New device inserted */
void (*remove) (struct ocp_device *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*save_state) (struct ocp_device *dev, u32 state); /* Save Device Context */
int (*suspend) (struct ocp_device *dev, u32 state); /* Device suspended */
int (*resume) (struct ocp_device *dev); /* Device woken up */
int (*enable_wake) (struct ocp_device *dev, u32 state, int enable); /* Enable wake event */
struct device_driver driver;
};
#define to_ocp_dev(n) container_of(n, struct ocp_device, dev)
#define to_ocp_driver(n) container_of(n, struct ocp_driver, driver)
extern int ocp_register_driver(struct ocp_driver *drv);
extern void ocp_unregister_driver(struct ocp_driver *drv);
#define ocp_dev_g(n) list_entry(n, struct ocp_device, global_list)
#define ocp_for_each_dev(dev) \
for(dev = ocp_dev_g(ocp_devices.next); dev != ocp_dev_g(&ocp_devices); dev = ocp_dev_g(dev->global_list.next))
#define to_ocp_dev(n) container_of(n, struct ocp_device, dev)
#define to_ocp_drv(n) container_of(n, struct ocp_driver, driver)
/* Similar to the helpers above, these manipulate per-ocp_dev
* driver-specific data. Currently stored as ocp_dev::ocpdev,
......@@ -131,45 +132,13 @@ extern void ocp_unregister_driver(struct ocp_driver *drv);
static inline void *
ocp_get_drvdata(struct ocp_device *pdev)
{
return pdev->ocpdev;
return pdev->drvdata;
}
static inline void
ocp_set_drvdata(struct ocp_device *pdev, void *data)
{
pdev->ocpdev = data;
}
/*
* a helper function which helps ensure correct pci_driver
* setup and cleanup for commonly-encountered hotplug/modular cases
*
* This MUST stay in a header, as it checks for -DMODULE
*/
static inline int ocp_module_init(struct ocp_driver *drv)
{
int rc = ocp_register_driver(drv);
if (rc > 0)
return 0;
/* iff CONFIG_HOTPLUG and built into kernel, we should
* leave the driver around for future hotplug events.
* For the module case, a hotplug daemon of some sort
* should load a module in response to an insert event. */
#if defined(CONFIG_HOTPLUG) && !defined(MODULE)
if (rc == 0)
return 0;
#else
if (rc == 0)
rc = -ENODEV;
#endif
/* if we get here, we need to clean up pci driver instance
* and return some sort of error */
ocp_unregister_driver (drv);
return rc;
pdev->drvdata = data;
}
#if defined (CONFIG_PM)
......@@ -180,26 +149,56 @@ static inline int ocp_module_init(struct ocp_driver *drv)
static inline void
ocp_force_power_off(struct ocp_device *odev)
{
mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) | odev->pm);
mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) | odev->def->pm);
}
static inline void
ocp_force_power_on(struct ocp_device *odev)
{
mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) & ~odev->pm);
mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) & ~odev->def->pm);
}
#else
#define ocp_force_power_off(x) (void)(x)
#define ocp_force_power_on(x) (void)(x)
#endif
extern void ocp_init(void);
extern struct bus_type ocp_bus_type;
extern struct ocp_device *ocp_get_dev(unsigned int device, int index);
extern unsigned int ocp_get_num(unsigned int device);
/* Register/Unregister an OCP driver */
extern int ocp_register_driver(struct ocp_driver *drv);
extern void ocp_unregister_driver(struct ocp_driver *drv);
extern int ocp_generic_suspend(struct ocp_device *pdev, u32 state);
extern int ocp_generic_resume(struct ocp_device *pdev);
/* Build list of devices */
extern int ocp_early_init(void) __init;
/* Find a device by index */
extern struct ocp_device *ocp_find_device(unsigned int vendor, unsigned int function, int index);
/* Get a def by index */
extern struct ocp_def *ocp_get_one_device(unsigned int vendor, unsigned int function, int index);
/* Add a device by index */
extern int ocp_add_one_device(struct ocp_def *def);
/* Remove a device by index */
extern int ocp_remove_one_device(unsigned int vendor, unsigned int function, int index);
/* Iterate over devices and execute a routine */
extern void ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg);
/* Sysfs support */
#define OCP_SYSFS_ADDTL(type, format, name, field) \
static ssize_t \
show_##name##_##field(struct device *dev, char *buf) \
{ \
struct ocp_device *odev = to_ocp_dev(dev); \
type *add = odev->def->additions; \
\
return sprintf(buf, format, add->field); \
} \
static DEVICE_ATTR(name##_##field, S_IRUGO, show_##name##_##field, NULL);
#ifdef CONFIG_IBM_OCP
#include <asm/ibm_ocp.h>
#endif
#endif /* __OCP_H__ */
#endif /* __KERNEL__ */
/*
* FILE NAME: ocp_ids.h
* ocp_ids.h
*
* BRIEF MODULE DESCRIPTION:
* OCP device ids based on the ideas from PCI
*
* Maintained by: Armin <akuster@mvista.com>
* The numbers below are almost completely arbitrary, and in fact
* strings might work better. -- paulus
*
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Version 1.0 08/22/02 -Armin
* initial release
* 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; either version 2 of the License, or (at your
* option) any later version.
*/
/*
......@@ -42,8 +23,9 @@
#define OCP_VENDOR_INVALID 0x0000
#define OCP_VENDOR_ARM 0x0004
#define OCP_VENDOR_FREESCALE 0x1057
#define OCP_VENDOR_IBM 0x1014
#define OCP_VENDOR_MOTOROLA 0x1057
#define OCP_VENDOR_MOTOROLA OCP_VENDOR_FREESCALE
#define OCP_VENDOR_XILINX 0x10ee
#define OCP_VENDOR_UNKNOWN 0xFFFF
......@@ -53,33 +35,20 @@
#define OCP_FUNC_INVALID 0x0000
/* system 0x0001 - 0x001F */
#define OCP_FUNC_UIC 0x0001
/* Timers 0x0020 - 0x002F */
#define OCP_FUNC_GPT 0x0020 /* General purpose timers */
#define OCP_FUNC_RTC 0x0021
/* Serial 0x0030 - 0x006F*/
#define OCP_FUNC_16550 0x0031
#define OCP_FUNC_SSP 0x0032 /* sync serial port */
#define OCP_FUNC_SCP 0x0033 /* serial controller port */
#define OCP_FUNC_SCC 0x0034 /* serial contoller */
#define OCP_FUNC_SCI 0x0035 /* Smart card */
#define OCP_FUNC_IIC 0x0040
#define OCP_FUNC_USB 0x0050
#define OCP_FUNC_IR 0x0060
#define OCP_FUNC_IIC 0x0032
#define OCP_FUNC_USB 0x0033
/* Memory devices 0x0090 - 0x009F */
#define OCP_FUNC_SDRAM 0x0091
#define OCP_FUNC_DMA 0x0092
#define OCP_FUNC_MAL 0x0090
/* Display 0x00A0 - 0x00AF */
#define OCP_FUNC_VIDEO 0x00A0
#define OCP_FUNC_LED 0x00A1
#define OCP_FUNC_LCD 0x00A2
/* Sound 0x00B0 - 0x00BF */
#define OCP_FUNC_AUDIO 0x00B0
/* Mass Storage 0x00C0 - 0xxCF */
#define OCP_FUNC_IDE 0x00C0
......@@ -87,17 +56,15 @@
/* Misc 0x00D0 - 0x00DF*/
#define OCP_FUNC_GPIO 0x00D0
#define OCP_FUNC_ZMII 0x00D1
#define OCP_FUNC_PERFMON 0x00D2 /* Performance Monitor */
#define OCP_FUNC_RGMII 0x00D3
#define OCP_FUNC_TAH 0x00D4
/* Network 0x0200 - 0x02FF */
#define OCP_FUNC_EMAC 0x0200
#define OCP_FUNC_ENET 0x0201 /* TSEC & FEC */
/* Bridge devices 0xE00 - 0xEFF */
#define OCP_FUNC_HOST 0x0E00
#define OCP_FUNC_DCR 0x0E01
#define OCP_FUNC_OPB 0x0E02
#define OCP_FUNC_PHY 0x0E03
#define OCP_FUNC_EXT 0x0E04
#define OCP_FUNC_PCI 0x0E05
#define OCP_FUNC_PLB 0x0E06
#define OCP_FUNC_OPB 0x0E00
#define OCP_FUNC_UNKNOWN 0xFFFF
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment