Commit 0dc4ac05 authored by Paul Mackerras's avatar Paul Mackerras

PPC32: Add support for PPC 4xx on-chip devices using the generic

device model.
parent 5da56ed9
......@@ -55,6 +55,7 @@ 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 pImage vmlinux.sm
......
#
# Makefile for the linux kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
#
# NB: cribbed from the drivers/sbus/Makefile -- PMM
export-objs := ocp.o ocp-driver.o ocp-probe.o
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 */
strcpy(dev->dev.name, dev->name);
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");
strcpy(b->name, "Host/OCP Bridge");
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 */
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