MIPS: ath79: Improve the DDR controller interface

The DDR controller need to be used by the IRQ controller to flush
the write buffer of some devices before running the IRQ handler.
It is also used by the PCI controller to setup the PCI memory windows.

The current interface used to access the DDR controller doesn't
provides any useful abstraction and simply rely on a shared global
pointer.

Replace this by a simple API to setup the PCI memory windows and use
the write buffer flush independently of the SoC type. That remove the
need for the shared global pointer, simplify the IRQ handler code.

[ralf@linux-mips.org: Folded in Alban Bedel's follup fix.]

Signed-off-by: Alban Bedel <albeu@free.fr>
Cc: linux-mips@linux-mips.org
Cc: Andrew Bresticker <abrestic@chromium.org>
Cc: Qais Yousef <qais.yousef@imgtec.com>
Cc: Wolfram Sang <wsa@the-dreams.de>
Cc: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Cc: Gabor Juhos <juhosg@openwrt.org>
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/9773/
Patchwork: http://patchwork.linux-mips.org/patch/10543/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Alban Bedel 2015-04-19 14:30:03 +02:00 committed by Ralf Baechle
parent 626a0695a6
commit 24b0e3e84f
6 changed files with 66 additions and 125 deletions

View File

@ -38,11 +38,27 @@ unsigned int ath79_soc_rev;
void __iomem *ath79_pll_base;
void __iomem *ath79_reset_base;
EXPORT_SYMBOL_GPL(ath79_reset_base);
void __iomem *ath79_ddr_base;
static void __iomem *ath79_ddr_base;
static void __iomem *ath79_ddr_wb_flush_base;
static void __iomem *ath79_ddr_pci_win_base;
void ath79_ddr_ctrl_init(void)
{
ath79_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE,
AR71XX_DDR_CTRL_SIZE);
if (soc_is_ar71xx() || soc_is_ar934x()) {
ath79_ddr_wb_flush_base = ath79_ddr_base + 0x9c;
ath79_ddr_pci_win_base = ath79_ddr_base + 0x7c;
} else {
ath79_ddr_wb_flush_base = ath79_ddr_base + 0x7c;
ath79_ddr_pci_win_base = 0;
}
}
EXPORT_SYMBOL_GPL(ath79_ddr_ctrl_init);
void ath79_ddr_wb_flush(u32 reg)
{
void __iomem *flush_reg = ath79_ddr_base + reg;
void __iomem *flush_reg = ath79_ddr_wb_flush_base + reg;
/* Flush the DDR write buffer. */
__raw_writel(0x1, flush_reg);
@ -56,6 +72,21 @@ void ath79_ddr_wb_flush(u32 reg)
}
EXPORT_SYMBOL_GPL(ath79_ddr_wb_flush);
void ath79_ddr_set_pci_windows(void)
{
BUG_ON(!ath79_ddr_pci_win_base);
__raw_writel(AR71XX_PCI_WIN0_OFFS, ath79_ddr_pci_win_base + 0);
__raw_writel(AR71XX_PCI_WIN1_OFFS, ath79_ddr_pci_win_base + 1);
__raw_writel(AR71XX_PCI_WIN2_OFFS, ath79_ddr_pci_win_base + 2);
__raw_writel(AR71XX_PCI_WIN3_OFFS, ath79_ddr_pci_win_base + 3);
__raw_writel(AR71XX_PCI_WIN4_OFFS, ath79_ddr_pci_win_base + 4);
__raw_writel(AR71XX_PCI_WIN5_OFFS, ath79_ddr_pci_win_base + 5);
__raw_writel(AR71XX_PCI_WIN6_OFFS, ath79_ddr_pci_win_base + 6);
__raw_writel(AR71XX_PCI_WIN7_OFFS, ath79_ddr_pci_win_base + 7);
}
EXPORT_SYMBOL_GPL(ath79_ddr_set_pci_windows);
void ath79_device_reset_set(u32 mask)
{
unsigned long flags;

View File

@ -22,6 +22,7 @@
void ath79_clocks_init(void);
unsigned long ath79_get_sys_clk_rate(const char *id);
void ath79_ddr_ctrl_init(void);
void ath79_ddr_wb_flush(unsigned int reg);
void ath79_gpio_function_enable(u32 mask);

View File

@ -24,9 +24,6 @@
#include <asm/mach-ath79/ar71xx_regs.h>
#include "common.h"
static void (*ath79_ip2_handler)(void);
static void (*ath79_ip3_handler)(void);
static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc)
{
void __iomem *base = ath79_reset_base;
@ -129,10 +126,10 @@ static void ar934x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc)
status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS);
if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) {
ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_PCIE);
ath79_ddr_wb_flush(3);
generic_handle_irq(ATH79_IP2_IRQ(0));
} else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) {
ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_WMAC);
ath79_ddr_wb_flush(4);
generic_handle_irq(ATH79_IP2_IRQ(1));
} else {
spurious_interrupt();
@ -235,128 +232,50 @@ static void qca955x_irq_init(void)
irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch);
}
asmlinkage void plat_irq_dispatch(void)
{
unsigned long pending;
pending = read_c0_status() & read_c0_cause() & ST0_IM;
if (pending & STATUSF_IP7)
do_IRQ(ATH79_CPU_IRQ(7));
else if (pending & STATUSF_IP2)
ath79_ip2_handler();
else if (pending & STATUSF_IP4)
do_IRQ(ATH79_CPU_IRQ(4));
else if (pending & STATUSF_IP5)
do_IRQ(ATH79_CPU_IRQ(5));
else if (pending & STATUSF_IP3)
ath79_ip3_handler();
else if (pending & STATUSF_IP6)
do_IRQ(ATH79_CPU_IRQ(6));
else
spurious_interrupt();
}
/*
* The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
* these devices typically allocate coherent DMA memory, however the
* DMA controller may still have some unsynchronized data in the FIFO.
* Issue a flush in the handlers to ensure that the driver sees
* the update.
*
* This array map the interrupt lines to the DDR write buffer channels.
*/
static void ath79_default_ip2_handler(void)
{
do_IRQ(ATH79_CPU_IRQ(2));
}
static unsigned irq_wb_chan[8] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
static void ath79_default_ip3_handler(void)
asmlinkage void plat_irq_dispatch(void)
{
do_IRQ(ATH79_CPU_IRQ(3));
}
unsigned long pending;
int irq;
static void ar71xx_ip2_handler(void)
{
ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_PCI);
do_IRQ(ATH79_CPU_IRQ(2));
}
pending = read_c0_status() & read_c0_cause() & ST0_IM;
static void ar724x_ip2_handler(void)
{
ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_PCIE);
do_IRQ(ATH79_CPU_IRQ(2));
}
if (!pending) {
spurious_interrupt();
return;
}
static void ar913x_ip2_handler(void)
{
ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_WMAC);
do_IRQ(ATH79_CPU_IRQ(2));
}
static void ar933x_ip2_handler(void)
{
ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_WMAC);
do_IRQ(ATH79_CPU_IRQ(2));
}
static void ar71xx_ip3_handler(void)
{
ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_USB);
do_IRQ(ATH79_CPU_IRQ(3));
}
static void ar724x_ip3_handler(void)
{
ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_USB);
do_IRQ(ATH79_CPU_IRQ(3));
}
static void ar913x_ip3_handler(void)
{
ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_USB);
do_IRQ(ATH79_CPU_IRQ(3));
}
static void ar933x_ip3_handler(void)
{
ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_USB);
do_IRQ(ATH79_CPU_IRQ(3));
}
static void ar934x_ip3_handler(void)
{
ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_USB);
do_IRQ(ATH79_CPU_IRQ(3));
pending >>= CAUSEB_IP;
while (pending) {
irq = fls(pending) - 1;
if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
ath79_ddr_wb_flush(irq_wb_chan[irq]);
do_IRQ(MIPS_CPU_IRQ_BASE + irq);
pending &= ~BIT(irq);
}
}
void __init arch_init_irq(void)
{
if (soc_is_ar71xx()) {
ath79_ip2_handler = ar71xx_ip2_handler;
ath79_ip3_handler = ar71xx_ip3_handler;
} else if (soc_is_ar724x()) {
ath79_ip2_handler = ar724x_ip2_handler;
ath79_ip3_handler = ar724x_ip3_handler;
} else if (soc_is_ar913x()) {
ath79_ip2_handler = ar913x_ip2_handler;
ath79_ip3_handler = ar913x_ip3_handler;
} else if (soc_is_ar933x()) {
ath79_ip2_handler = ar933x_ip2_handler;
ath79_ip3_handler = ar933x_ip3_handler;
if (soc_is_ar71xx() || soc_is_ar724x() ||
soc_is_ar913x() || soc_is_ar933x()) {
irq_wb_chan[2] = 3;
irq_wb_chan[3] = 2;
} else if (soc_is_ar934x()) {
ath79_ip2_handler = ath79_default_ip2_handler;
ath79_ip3_handler = ar934x_ip3_handler;
} else if (soc_is_qca955x()) {
ath79_ip2_handler = ath79_default_ip2_handler;
ath79_ip3_handler = ath79_default_ip3_handler;
} else {
BUG();
irq_wb_chan[3] = 2;
}
mips_cpu_irq_init();

View File

@ -200,8 +200,7 @@ void __init plat_mem_setup(void)
AR71XX_RESET_SIZE);
ath79_pll_base = ioremap_nocache(AR71XX_PLL_BASE,
AR71XX_PLL_SIZE);
ath79_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE,
AR71XX_DDR_CTRL_SIZE);
ath79_ddr_ctrl_init();
ath79_detect_sys_type();
detect_memory_region(0, ATH79_MEM_SIZE_MIN, ATH79_MEM_SIZE_MAX);

View File

@ -115,7 +115,8 @@ static inline int soc_is_qca955x(void)
return soc_is_qca9556() || soc_is_qca9558();
}
extern void __iomem *ath79_ddr_base;
void ath79_ddr_set_pci_windows(void);
extern void __iomem *ath79_pll_base;
extern void __iomem *ath79_reset_base;

View File

@ -318,23 +318,13 @@ static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc)
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);
ath79_ddr_set_pci_windows();
mdelay(100);
}