forked from Minki/linux
bd0b9ac405
Most interrupt flow handlers do not use the irq argument. Those few which use it can retrieve the irq number from the irq descriptor. Remove the argument. Search and replace was done with coccinelle and some extra helper scripts around it. Thanks to Julia for her help! Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Julia Lawall <Julia.Lawall@lip6.fr> Cc: Jiang Liu <jiang.liu@linux.intel.com>
405 lines
9.9 KiB
C
405 lines
9.9 KiB
C
/*
|
|
* Atheros AR71xx PCI host controller driver
|
|
*
|
|
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
|
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
|
*
|
|
* Parts of this file are based on Atheros' 2.6.15 BSP
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/resource.h>
|
|
#include <linux/types.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_regs.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <asm/mach-ath79/ar71xx_regs.h>
|
|
#include <asm/mach-ath79/ath79.h>
|
|
|
|
#define AR71XX_PCI_REG_CRP_AD_CBE 0x00
|
|
#define AR71XX_PCI_REG_CRP_WRDATA 0x04
|
|
#define AR71XX_PCI_REG_CRP_RDDATA 0x08
|
|
#define AR71XX_PCI_REG_CFG_AD 0x0c
|
|
#define AR71XX_PCI_REG_CFG_CBE 0x10
|
|
#define AR71XX_PCI_REG_CFG_WRDATA 0x14
|
|
#define AR71XX_PCI_REG_CFG_RDDATA 0x18
|
|
#define AR71XX_PCI_REG_PCI_ERR 0x1c
|
|
#define AR71XX_PCI_REG_PCI_ERR_ADDR 0x20
|
|
#define AR71XX_PCI_REG_AHB_ERR 0x24
|
|
#define AR71XX_PCI_REG_AHB_ERR_ADDR 0x28
|
|
|
|
#define AR71XX_PCI_CRP_CMD_WRITE 0x00010000
|
|
#define AR71XX_PCI_CRP_CMD_READ 0x00000000
|
|
#define AR71XX_PCI_CFG_CMD_READ 0x0000000a
|
|
#define AR71XX_PCI_CFG_CMD_WRITE 0x0000000b
|
|
|
|
#define AR71XX_PCI_INT_CORE BIT(4)
|
|
#define AR71XX_PCI_INT_DEV2 BIT(2)
|
|
#define AR71XX_PCI_INT_DEV1 BIT(1)
|
|
#define AR71XX_PCI_INT_DEV0 BIT(0)
|
|
|
|
#define AR71XX_PCI_IRQ_COUNT 5
|
|
|
|
struct ar71xx_pci_controller {
|
|
void __iomem *cfg_base;
|
|
int irq;
|
|
int irq_base;
|
|
struct pci_controller pci_ctrl;
|
|
struct resource io_res;
|
|
struct resource mem_res;
|
|
};
|
|
|
|
/* Byte lane enable bits */
|
|
static const u8 ar71xx_pci_ble_table[4][4] = {
|
|
{0x0, 0xf, 0xf, 0xf},
|
|
{0xe, 0xd, 0xb, 0x7},
|
|
{0xc, 0xf, 0x3, 0xf},
|
|
{0xf, 0xf, 0xf, 0xf},
|
|
};
|
|
|
|
static const u32 ar71xx_pci_read_mask[8] = {
|
|
0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0
|
|
};
|
|
|
|
static inline u32 ar71xx_pci_get_ble(int where, int size, int local)
|
|
{
|
|
u32 t;
|
|
|
|
t = ar71xx_pci_ble_table[size & 3][where & 3];
|
|
BUG_ON(t == 0xf);
|
|
t <<= (local) ? 20 : 4;
|
|
|
|
return t;
|
|
}
|
|
|
|
static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn,
|
|
int where)
|
|
{
|
|
u32 ret;
|
|
|
|
if (!bus->number) {
|
|
/* type 0 */
|
|
ret = (1 << PCI_SLOT(devfn)) | (PCI_FUNC(devfn) << 8) |
|
|
(where & ~3);
|
|
} else {
|
|
/* type 1 */
|
|
ret = (bus->number << 16) | (PCI_SLOT(devfn) << 11) |
|
|
(PCI_FUNC(devfn) << 8) | (where & ~3) | 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline struct ar71xx_pci_controller *
|
|
pci_bus_to_ar71xx_controller(struct pci_bus *bus)
|
|
{
|
|
struct pci_controller *hose;
|
|
|
|
hose = (struct pci_controller *) bus->sysdata;
|
|
return container_of(hose, struct ar71xx_pci_controller, pci_ctrl);
|
|
}
|
|
|
|
static int ar71xx_pci_check_error(struct ar71xx_pci_controller *apc, int quiet)
|
|
{
|
|
void __iomem *base = apc->cfg_base;
|
|
u32 pci_err;
|
|
u32 ahb_err;
|
|
|
|
pci_err = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR) & 3;
|
|
if (pci_err) {
|
|
if (!quiet) {
|
|
u32 addr;
|
|
|
|
addr = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR_ADDR);
|
|
pr_crit("ar71xx: %s bus error %d at addr 0x%x\n",
|
|
"PCI", pci_err, addr);
|
|
}
|
|
|
|
/* clear PCI error status */
|
|
__raw_writel(pci_err, base + AR71XX_PCI_REG_PCI_ERR);
|
|
}
|
|
|
|
ahb_err = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR) & 1;
|
|
if (ahb_err) {
|
|
if (!quiet) {
|
|
u32 addr;
|
|
|
|
addr = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR_ADDR);
|
|
pr_crit("ar71xx: %s bus error %d at addr 0x%x\n",
|
|
"AHB", ahb_err, addr);
|
|
}
|
|
|
|
/* clear AHB error status */
|
|
__raw_writel(ahb_err, base + AR71XX_PCI_REG_AHB_ERR);
|
|
}
|
|
|
|
return !!(ahb_err | pci_err);
|
|
}
|
|
|
|
static inline void ar71xx_pci_local_write(struct ar71xx_pci_controller *apc,
|
|
int where, int size, u32 value)
|
|
{
|
|
void __iomem *base = apc->cfg_base;
|
|
u32 ad_cbe;
|
|
|
|
value = value << (8 * (where & 3));
|
|
|
|
ad_cbe = AR71XX_PCI_CRP_CMD_WRITE | (where & ~3);
|
|
ad_cbe |= ar71xx_pci_get_ble(where, size, 1);
|
|
|
|
__raw_writel(ad_cbe, base + AR71XX_PCI_REG_CRP_AD_CBE);
|
|
__raw_writel(value, base + AR71XX_PCI_REG_CRP_WRDATA);
|
|
}
|
|
|
|
static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus,
|
|
unsigned int devfn,
|
|
int where, int size, u32 cmd)
|
|
{
|
|
struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus);
|
|
void __iomem *base = apc->cfg_base;
|
|
u32 addr;
|
|
|
|
addr = ar71xx_pci_bus_addr(bus, devfn, where);
|
|
|
|
__raw_writel(addr, base + AR71XX_PCI_REG_CFG_AD);
|
|
__raw_writel(cmd | ar71xx_pci_get_ble(where, size, 0),
|
|
base + AR71XX_PCI_REG_CFG_CBE);
|
|
|
|
return ar71xx_pci_check_error(apc, 1);
|
|
}
|
|
|
|
static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
|
|
int where, int size, u32 *value)
|
|
{
|
|
struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus);
|
|
void __iomem *base = apc->cfg_base;
|
|
u32 data;
|
|
int err;
|
|
int ret;
|
|
|
|
ret = PCIBIOS_SUCCESSFUL;
|
|
data = ~0;
|
|
|
|
err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
|
|
AR71XX_PCI_CFG_CMD_READ);
|
|
if (err)
|
|
ret = PCIBIOS_DEVICE_NOT_FOUND;
|
|
else
|
|
data = __raw_readl(base + AR71XX_PCI_REG_CFG_RDDATA);
|
|
|
|
*value = (data >> (8 * (where & 3))) & ar71xx_pci_read_mask[size & 7];
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
|
|
int where, int size, u32 value)
|
|
{
|
|
struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus);
|
|
void __iomem *base = apc->cfg_base;
|
|
int err;
|
|
int ret;
|
|
|
|
value = value << (8 * (where & 3));
|
|
ret = PCIBIOS_SUCCESSFUL;
|
|
|
|
err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
|
|
AR71XX_PCI_CFG_CMD_WRITE);
|
|
if (err)
|
|
ret = PCIBIOS_DEVICE_NOT_FOUND;
|
|
else
|
|
__raw_writel(value, base + AR71XX_PCI_REG_CFG_WRDATA);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct pci_ops ar71xx_pci_ops = {
|
|
.read = ar71xx_pci_read_config,
|
|
.write = ar71xx_pci_write_config,
|
|
};
|
|
|
|
static void ar71xx_pci_irq_handler(struct irq_desc *desc)
|
|
{
|
|
struct ar71xx_pci_controller *apc;
|
|
void __iomem *base = ath79_reset_base;
|
|
u32 pending;
|
|
|
|
apc = irq_desc_get_handler_data(desc);
|
|
|
|
pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) &
|
|
__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
|
|
if (pending & AR71XX_PCI_INT_DEV0)
|
|
generic_handle_irq(apc->irq_base + 0);
|
|
|
|
else if (pending & AR71XX_PCI_INT_DEV1)
|
|
generic_handle_irq(apc->irq_base + 1);
|
|
|
|
else if (pending & AR71XX_PCI_INT_DEV2)
|
|
generic_handle_irq(apc->irq_base + 2);
|
|
|
|
else if (pending & AR71XX_PCI_INT_CORE)
|
|
generic_handle_irq(apc->irq_base + 4);
|
|
|
|
else
|
|
spurious_interrupt();
|
|
}
|
|
|
|
static void ar71xx_pci_irq_unmask(struct irq_data *d)
|
|
{
|
|
struct ar71xx_pci_controller *apc;
|
|
unsigned int irq;
|
|
void __iomem *base = ath79_reset_base;
|
|
u32 t;
|
|
|
|
apc = irq_data_get_irq_chip_data(d);
|
|
irq = d->irq - apc->irq_base;
|
|
|
|
t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
|
|
/* flush write */
|
|
__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
}
|
|
|
|
static void ar71xx_pci_irq_mask(struct irq_data *d)
|
|
{
|
|
struct ar71xx_pci_controller *apc;
|
|
unsigned int irq;
|
|
void __iomem *base = ath79_reset_base;
|
|
u32 t;
|
|
|
|
apc = irq_data_get_irq_chip_data(d);
|
|
irq = d->irq - apc->irq_base;
|
|
|
|
t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
|
|
/* flush write */
|
|
__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
}
|
|
|
|
static struct irq_chip ar71xx_pci_irq_chip = {
|
|
.name = "AR71XX PCI",
|
|
.irq_mask = ar71xx_pci_irq_mask,
|
|
.irq_unmask = ar71xx_pci_irq_unmask,
|
|
.irq_mask_ack = ar71xx_pci_irq_mask,
|
|
};
|
|
|
|
static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc)
|
|
{
|
|
void __iomem *base = ath79_reset_base;
|
|
int i;
|
|
|
|
__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
|
|
__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS);
|
|
|
|
BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT);
|
|
|
|
apc->irq_base = ATH79_PCI_IRQ_BASE;
|
|
for (i = apc->irq_base;
|
|
i < apc->irq_base + AR71XX_PCI_IRQ_COUNT; i++) {
|
|
irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip,
|
|
handle_level_irq);
|
|
irq_set_chip_data(i, apc);
|
|
}
|
|
|
|
irq_set_chained_handler_and_data(apc->irq, ar71xx_pci_irq_handler,
|
|
apc);
|
|
}
|
|
|
|
static void ar71xx_pci_reset(void)
|
|
{
|
|
ath79_device_reset_set(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
|
|
mdelay(100);
|
|
|
|
ath79_device_reset_clear(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
|
|
mdelay(100);
|
|
|
|
ath79_ddr_set_pci_windows();
|
|
mdelay(100);
|
|
}
|
|
|
|
static int ar71xx_pci_probe(struct platform_device *pdev)
|
|
{
|
|
struct ar71xx_pci_controller *apc;
|
|
struct resource *res;
|
|
u32 t;
|
|
|
|
apc = devm_kzalloc(&pdev->dev, sizeof(struct ar71xx_pci_controller),
|
|
GFP_KERNEL);
|
|
if (!apc)
|
|
return -ENOMEM;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base");
|
|
apc->cfg_base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(apc->cfg_base))
|
|
return PTR_ERR(apc->cfg_base);
|
|
|
|
apc->irq = platform_get_irq(pdev, 0);
|
|
if (apc->irq < 0)
|
|
return -EINVAL;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base");
|
|
if (!res)
|
|
return -EINVAL;
|
|
|
|
apc->io_res.parent = res;
|
|
apc->io_res.name = "PCI IO space";
|
|
apc->io_res.start = res->start;
|
|
apc->io_res.end = res->end;
|
|
apc->io_res.flags = IORESOURCE_IO;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base");
|
|
if (!res)
|
|
return -EINVAL;
|
|
|
|
apc->mem_res.parent = res;
|
|
apc->mem_res.name = "PCI memory space";
|
|
apc->mem_res.start = res->start;
|
|
apc->mem_res.end = res->end;
|
|
apc->mem_res.flags = IORESOURCE_MEM;
|
|
|
|
ar71xx_pci_reset();
|
|
|
|
/* setup COMMAND register */
|
|
t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
|
|
| PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK;
|
|
ar71xx_pci_local_write(apc, PCI_COMMAND, 4, t);
|
|
|
|
/* clear bus errors */
|
|
ar71xx_pci_check_error(apc, 1);
|
|
|
|
ar71xx_pci_irq_init(apc);
|
|
|
|
apc->pci_ctrl.pci_ops = &ar71xx_pci_ops;
|
|
apc->pci_ctrl.mem_resource = &apc->mem_res;
|
|
apc->pci_ctrl.io_resource = &apc->io_res;
|
|
|
|
register_pci_controller(&apc->pci_ctrl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver ar71xx_pci_driver = {
|
|
.probe = ar71xx_pci_probe,
|
|
.driver = {
|
|
.name = "ar71xx-pci",
|
|
},
|
|
};
|
|
|
|
static int __init ar71xx_pci_init(void)
|
|
{
|
|
return platform_driver_register(&ar71xx_pci_driver);
|
|
}
|
|
|
|
postcore_initcall(ar71xx_pci_init);
|