forked from Minki/linux
e6b5be2be4
Here's the set of driver core patches for 3.19-rc1. They are dominated by the removal of the .owner field in platform drivers. They touch a lot of files, but they are "simple" changes, just removing a line in a structure. Other than that, a few minor driver core and debugfs changes. There are some ath9k patches coming in through this tree that have been acked by the wireless maintainers as they relied on the debugfs changes. Everything has been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlSOD20ACgkQMUfUDdst+ylLPACg2QrW1oHhdTMT9WI8jihlHVRM 53kAoLeteByQ3iVwWurwwseRPiWa8+MI =OVRS -----END PGP SIGNATURE----- Merge tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core update from Greg KH: "Here's the set of driver core patches for 3.19-rc1. They are dominated by the removal of the .owner field in platform drivers. They touch a lot of files, but they are "simple" changes, just removing a line in a structure. Other than that, a few minor driver core and debugfs changes. There are some ath9k patches coming in through this tree that have been acked by the wireless maintainers as they relied on the debugfs changes. Everything has been in linux-next for a while" * tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (324 commits) Revert "ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries" fs: debugfs: add forward declaration for struct device type firmware class: Deletion of an unnecessary check before the function call "vunmap" firmware loader: fix hung task warning dump devcoredump: provide a one-way disable function device: Add dev_<level>_once variants ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries ath: use seq_file api for ath9k debugfs files debugfs: add helper function to create device related seq_file drivers/base: cacheinfo: remove noisy error boot message Revert "core: platform: add warning if driver has no owner" drivers: base: support cpu cache information interface to userspace via sysfs drivers: base: add cpu_device_create to support per-cpu devices topology: replace custom attribute macros with standard DEVICE_ATTR* cpumask: factor out show_cpumap into separate helper function driver core: Fix unbalanced device reference in drivers_probe driver core: fix race with userland in device_add() sysfs/kernfs: make read requests on pre-alloc files use the buffer. sysfs/kernfs: allow attributes to request write buffer be pre-allocated. fs: sysfs: return EGBIG on write if offset is larger than file size ...
415 lines
11 KiB
C
415 lines
11 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(unsigned int irq, struct irq_desc *desc)
|
|
{
|
|
struct ar71xx_pci_controller *apc;
|
|
void __iomem *base = ath79_reset_base;
|
|
u32 pending;
|
|
|
|
apc = irq_get_handler_data(irq);
|
|
|
|
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_handler_data(apc->irq, apc);
|
|
irq_set_chained_handler(apc->irq, ar71xx_pci_irq_handler);
|
|
}
|
|
|
|
static void ar71xx_pci_reset(void)
|
|
{
|
|
void __iomem *ddr_base = ath79_ddr_base;
|
|
|
|
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);
|
|
|
|
__raw_writel(AR71XX_PCI_WIN0_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN0);
|
|
__raw_writel(AR71XX_PCI_WIN1_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN1);
|
|
__raw_writel(AR71XX_PCI_WIN2_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN2);
|
|
__raw_writel(AR71XX_PCI_WIN3_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN3);
|
|
__raw_writel(AR71XX_PCI_WIN4_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN4);
|
|
__raw_writel(AR71XX_PCI_WIN5_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN5);
|
|
__raw_writel(AR71XX_PCI_WIN6_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN6);
|
|
__raw_writel(AR71XX_PCI_WIN7_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN7);
|
|
|
|
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);
|