135 lines
3.2 KiB
C
135 lines
3.2 KiB
C
|
/*
|
||
|
* (c) 2010 STMicroelectronics Limited
|
||
|
*
|
||
|
* Author: David Mckay <david.mckay@st.com>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*
|
||
|
* SH4 specific glue to join up the stm pci driver in drivers/stm/
|
||
|
* to the sh specific PCI arch code. There will be a corresponding version
|
||
|
* of this file for the ARM and ST231 as well. This allows the core code
|
||
|
* to be maintained seperately rather than copying the driver.
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/pci.h>
|
||
|
#include <linux/pci_regs.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/stm/platform.h>
|
||
|
#include <linux/stm/pci-glue.h>
|
||
|
#include <linux/gpio.h>
|
||
|
#include <linux/cache.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/list.h>
|
||
|
#include <asm/clock.h>
|
||
|
|
||
|
struct stm_pci_chan_info {
|
||
|
struct pci_channel chan;
|
||
|
enum stm_pci_type type;
|
||
|
int legacy_irq;
|
||
|
struct platform_device *pdev;
|
||
|
};
|
||
|
|
||
|
|
||
|
/* Use container_of magic to get data */
|
||
|
static struct stm_pci_chan_info *bus_to_channel_info(struct pci_bus *bus)
|
||
|
{
|
||
|
struct pci_channel *hose = bus->sysdata;
|
||
|
|
||
|
return container_of(hose, struct stm_pci_chan_info, chan);
|
||
|
}
|
||
|
|
||
|
/* Given a pci_bus, return the corresponding platform data */
|
||
|
struct platform_device *stm_pci_bus_to_platform(struct pci_bus *bus)
|
||
|
{
|
||
|
struct stm_pci_chan_info *info;
|
||
|
|
||
|
info = bus_to_channel_info(bus);
|
||
|
if (!info)
|
||
|
return NULL;
|
||
|
|
||
|
return info->pdev;
|
||
|
}
|
||
|
|
||
|
int __devinit stm_pci_register_controller(struct platform_device *pdev,
|
||
|
struct pci_ops *config_ops,
|
||
|
enum stm_pci_type type)
|
||
|
{
|
||
|
struct stm_pci_chan_info *info;
|
||
|
struct resource *res;
|
||
|
|
||
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||
|
if (!info)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
/* Set up the sh board channel to point at the platform data we have
|
||
|
* passed in
|
||
|
*/
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||
|
(type == STM_PCI_EXPRESS) ?
|
||
|
"pcie memory" : "pci memory");
|
||
|
if (!res)
|
||
|
return -ENXIO;
|
||
|
info->chan.mem_resource = res;
|
||
|
|
||
|
/* Same for IO channel */
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
|
||
|
(type == STM_PCI_EXPRESS) ?
|
||
|
"pcie io" : "pci io");
|
||
|
if (!res)
|
||
|
return -ENXIO;
|
||
|
info->chan.io_resource = res;
|
||
|
|
||
|
/* Put some none zero value here to stop the generic code whining
|
||
|
* about not having io_map_base defined
|
||
|
*/
|
||
|
info->chan.io_map_base = ~0;
|
||
|
|
||
|
if (type == STM_PCI_EXPRESS) {
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
||
|
"pcie inta");
|
||
|
if (!res)
|
||
|
return -ENXIO;
|
||
|
info->legacy_irq = res->start;
|
||
|
} else
|
||
|
info->legacy_irq = -EINVAL;
|
||
|
|
||
|
info->type = type;
|
||
|
info->pdev = pdev;
|
||
|
info->chan.pci_ops = config_ops;
|
||
|
|
||
|
register_pci_controller(&info->chan);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
enum stm_pci_type stm_pci_controller_type(struct pci_bus *bus)
|
||
|
{
|
||
|
struct stm_pci_chan_info *info;
|
||
|
|
||
|
info = bus_to_channel_info(bus);
|
||
|
|
||
|
BUG_ON(!info);
|
||
|
|
||
|
return info->type;
|
||
|
}
|
||
|
|
||
|
int stm_pci_legacy_irq(struct pci_dev *dev)
|
||
|
{
|
||
|
struct stm_pci_chan_info *info;
|
||
|
|
||
|
info = bus_to_channel_info(dev->bus);
|
||
|
if (info == NULL)
|
||
|
return -EINVAL;
|
||
|
|
||
|
return info->legacy_irq;
|
||
|
}
|