forked from Minki/linux
9f800363bb
Lots of isolated bug fixes that were not found to be important enough to be submitted before the merge window or backported into stable kernels. The vast majority of these came out of Arnd's randconfig testing and just prevents running into build-time bugs in configurations that we do not care about in practice. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIVAwUAUz/yEmCrR//JCVInAQIDsBAAu9uUC/uuc77953rsRqXPOCqjG4Q4g7Y+ HGxuztTGGJN6eglK7+aRKbmSlZck6KQykevm+OYnoINcGyazXmajkUnbaVvgNCU9 iRyRLkLjilDWBQXY5Ou3wK2WgyI4pMokRYIkp+MpQHQ5IlvJ5707IYj+FswdK5kT npbcP+L5oJ13afVnI18uflapr2ecXGdvfuEZw3sWpKcfefutxmEVYzRUBkNgj5Pd bva9GcWuA/ymRJR1XQmXh7EE+kqzGX5P0hFfaQsgtUwvY2Bv3fNia+GMLrf6pUGb Pl3rxyfo9VKoW0gbeVB7sk1rHTgh6ay2T8PBSz5dpyoR4A1n8BZQXPjUd7fBKv97 VRWMXRQz5sQ05FnvJFlV5CcYikf8GFOPooUhgY7Fo1sdoDawkAOQ1AJ4yhPsx86u V/S3o3pMWqDGnFMFmS95iAWW7Ru66XVYsPJnFktiLXt6SLlSAY52DzV6HlStF4hi O9dsIi5TsOxYhSWpMFZCxHK/I805zEjGOAyTYnCQB6Lwadg0mUiwdRJvp0YzcdDM X1mCsz8yHM3bbhvkxbqzwnBNgz24TkDPA8IvUGFtyxGF+5m8MgAzIKcGc4PKI6Gg I9M0oechC2dusvfflXFinvRhZMHMHi8+t58b/+29KrsacnE5vDmBFzeWGUkCXs5q oo4cWe14m6U= =KRJL -----END PGP SIGNATURE----- Merge tag 'fixes-non-critical-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM SoC non-critical bug fixes from Arnd Bergmann: "Lots of isolated bug fixes that were not found to be important enough to be submitted before the merge window or backported into stable kernels. The vast majority of these came out of Arnd's randconfig testing and just prevents running into build-time bugs in configurations that we do not care about in practice" * tag 'fixes-non-critical-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (75 commits) ARM: at91: fix a typo ARM: moxart: fix CPU selection ARM: tegra: fix board DT pinmux setup ARM: nspire: Fix compiler warning IXP4xx: Fix DMA masks. Revert "ARM: ixp4xx: Make dma_set_coherent_mask common, correct implementation" IXP4xx: Fix Goramo Multilink GPIO conversion. Revert "ARM: ixp4xx: fix gpio rework" ARM: tegra: make debug_ll code build for ARMv6 ARM: sunxi: fix build for THUMB2_KERNEL ARM: exynos: add missing include of linux/module.h ARM: exynos: fix l2x0 saved regs handling ARM: samsung: select CRC32 for SAMSUNG_PM_CHECK ARM: samsung: select ATAGS where necessary ARM: samsung: fix SAMSUNG_PM_DEBUG Kconfig logic ARM: samsung: allow serial driver to be disabled ARM: s5pv210: enable IDE support in MACH_TORBRECK ARM: s5p64x0: fix building with only one soc type ARM: s3c64xx: select power domains only when used ARM: s3c64xx: MACH_SMDK6400 needs HSMMC1 ...
409 lines
11 KiB
C
409 lines
11 KiB
C
/*
|
|
* Copyright 1999 - 2003 ARM Limited
|
|
* Copyright 2000 Deep Blue Solutions Ltd
|
|
* Copyright 2008 Cavium Networks
|
|
*
|
|
* This file 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/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/clockchips.h>
|
|
#include <linux/io.h>
|
|
#include <linux/irqchip/arm-gic.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/usb/ehci_pdriver.h>
|
|
#include <linux/usb/ohci_pdriver.h>
|
|
#include <asm/mach/arch.h>
|
|
#include <asm/mach/map.h>
|
|
#include <asm/mach/time.h>
|
|
#include <asm/mach/irq.h>
|
|
#include <asm/hardware/cache-l2x0.h>
|
|
#include "cns3xxx.h"
|
|
#include "core.h"
|
|
#include "pm.h"
|
|
|
|
static struct map_desc cns3xxx_io_desc[] __initdata = {
|
|
{
|
|
.virtual = CNS3XXX_TC11MP_SCU_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_TC11MP_SCU_BASE),
|
|
.length = SZ_8K,
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_TIMER1_2_3_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_TIMER1_2_3_BASE),
|
|
.length = SZ_4K,
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_MISC_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_MISC_BASE),
|
|
.length = SZ_4K,
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_PM_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PM_BASE),
|
|
.length = SZ_4K,
|
|
.type = MT_DEVICE,
|
|
#ifdef CONFIG_PCI
|
|
}, {
|
|
.virtual = CNS3XXX_PCIE0_HOST_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PCIE0_HOST_BASE),
|
|
.length = SZ_4K,
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_PCIE0_CFG0_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PCIE0_CFG0_BASE),
|
|
.length = SZ_64K, /* really 4 KiB at offset 32 KiB */
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_PCIE0_CFG1_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PCIE0_CFG1_BASE),
|
|
.length = SZ_16M,
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_PCIE1_HOST_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PCIE1_HOST_BASE),
|
|
.length = SZ_4K,
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_PCIE1_CFG0_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG0_BASE),
|
|
.length = SZ_64K, /* really 4 KiB at offset 32 KiB */
|
|
.type = MT_DEVICE,
|
|
}, {
|
|
.virtual = CNS3XXX_PCIE1_CFG1_BASE_VIRT,
|
|
.pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG1_BASE),
|
|
.length = SZ_16M,
|
|
.type = MT_DEVICE,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
void __init cns3xxx_map_io(void)
|
|
{
|
|
iotable_init(cns3xxx_io_desc, ARRAY_SIZE(cns3xxx_io_desc));
|
|
}
|
|
|
|
/* used by entry-macro.S */
|
|
void __init cns3xxx_init_irq(void)
|
|
{
|
|
gic_init(0, 29, IOMEM(CNS3XXX_TC11MP_GIC_DIST_BASE_VIRT),
|
|
IOMEM(CNS3XXX_TC11MP_GIC_CPU_BASE_VIRT));
|
|
}
|
|
|
|
void cns3xxx_power_off(void)
|
|
{
|
|
u32 __iomem *pm_base = IOMEM(CNS3XXX_PM_BASE_VIRT);
|
|
u32 clkctrl;
|
|
|
|
printk(KERN_INFO "powering system down...\n");
|
|
|
|
clkctrl = readl(pm_base + PM_SYS_CLK_CTRL_OFFSET);
|
|
clkctrl &= 0xfffff1ff;
|
|
clkctrl |= (0x5 << 9); /* Hibernate */
|
|
writel(clkctrl, pm_base + PM_SYS_CLK_CTRL_OFFSET);
|
|
|
|
}
|
|
|
|
/*
|
|
* Timer
|
|
*/
|
|
static void __iomem *cns3xxx_tmr1;
|
|
|
|
static void cns3xxx_timer_set_mode(enum clock_event_mode mode,
|
|
struct clock_event_device *clk)
|
|
{
|
|
unsigned long ctrl = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
int pclk = cns3xxx_cpu_clock() / 8;
|
|
int reload;
|
|
|
|
switch (mode) {
|
|
case CLOCK_EVT_MODE_PERIODIC:
|
|
reload = pclk * 20 / (3 * HZ) * 0x25000;
|
|
writel(reload, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET);
|
|
ctrl |= (1 << 0) | (1 << 2) | (1 << 9);
|
|
break;
|
|
case CLOCK_EVT_MODE_ONESHOT:
|
|
/* period set, and timer enabled in 'next_event' hook */
|
|
ctrl |= (1 << 2) | (1 << 9);
|
|
break;
|
|
case CLOCK_EVT_MODE_UNUSED:
|
|
case CLOCK_EVT_MODE_SHUTDOWN:
|
|
default:
|
|
ctrl = 0;
|
|
}
|
|
|
|
writel(ctrl, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
}
|
|
|
|
static int cns3xxx_timer_set_next_event(unsigned long evt,
|
|
struct clock_event_device *unused)
|
|
{
|
|
unsigned long ctrl = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
|
|
writel(evt, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET);
|
|
writel(ctrl | (1 << 0), cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct clock_event_device cns3xxx_tmr1_clockevent = {
|
|
.name = "cns3xxx timer1",
|
|
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
|
.set_mode = cns3xxx_timer_set_mode,
|
|
.set_next_event = cns3xxx_timer_set_next_event,
|
|
.rating = 350,
|
|
.cpumask = cpu_all_mask,
|
|
};
|
|
|
|
static void __init cns3xxx_clockevents_init(unsigned int timer_irq)
|
|
{
|
|
cns3xxx_tmr1_clockevent.irq = timer_irq;
|
|
clockevents_config_and_register(&cns3xxx_tmr1_clockevent,
|
|
(cns3xxx_cpu_clock() >> 3) * 1000000,
|
|
0xf, 0xffffffff);
|
|
}
|
|
|
|
/*
|
|
* IRQ handler for the timer
|
|
*/
|
|
static irqreturn_t cns3xxx_timer_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct clock_event_device *evt = &cns3xxx_tmr1_clockevent;
|
|
u32 __iomem *stat = cns3xxx_tmr1 + TIMER1_2_INTERRUPT_STATUS_OFFSET;
|
|
u32 val;
|
|
|
|
/* Clear the interrupt */
|
|
val = readl(stat);
|
|
writel(val & ~(1 << 2), stat);
|
|
|
|
evt->event_handler(evt);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static struct irqaction cns3xxx_timer_irq = {
|
|
.name = "timer",
|
|
.flags = IRQF_TIMER | IRQF_IRQPOLL,
|
|
.handler = cns3xxx_timer_interrupt,
|
|
};
|
|
|
|
/*
|
|
* Set up the clock source and clock events devices
|
|
*/
|
|
static void __init __cns3xxx_timer_init(unsigned int timer_irq)
|
|
{
|
|
u32 val;
|
|
u32 irq_mask;
|
|
|
|
/*
|
|
* Initialise to a known state (all timers off)
|
|
*/
|
|
|
|
/* disable timer1 and timer2 */
|
|
writel(0, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
/* stop free running timer3 */
|
|
writel(0, cns3xxx_tmr1 + TIMER_FREERUN_CONTROL_OFFSET);
|
|
|
|
/* timer1 */
|
|
writel(0x5C800, cns3xxx_tmr1 + TIMER1_COUNTER_OFFSET);
|
|
writel(0x5C800, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET);
|
|
|
|
writel(0, cns3xxx_tmr1 + TIMER1_MATCH_V1_OFFSET);
|
|
writel(0, cns3xxx_tmr1 + TIMER1_MATCH_V2_OFFSET);
|
|
|
|
/* mask irq, non-mask timer1 overflow */
|
|
irq_mask = readl(cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET);
|
|
irq_mask &= ~(1 << 2);
|
|
irq_mask |= 0x03;
|
|
writel(irq_mask, cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET);
|
|
|
|
/* down counter */
|
|
val = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
val |= (1 << 9);
|
|
writel(val, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
|
|
/* timer2 */
|
|
writel(0, cns3xxx_tmr1 + TIMER2_MATCH_V1_OFFSET);
|
|
writel(0, cns3xxx_tmr1 + TIMER2_MATCH_V2_OFFSET);
|
|
|
|
/* mask irq */
|
|
irq_mask = readl(cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET);
|
|
irq_mask |= ((1 << 3) | (1 << 4) | (1 << 5));
|
|
writel(irq_mask, cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET);
|
|
|
|
/* down counter */
|
|
val = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
val |= (1 << 10);
|
|
writel(val, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET);
|
|
|
|
/* Make irqs happen for the system timer */
|
|
setup_irq(timer_irq, &cns3xxx_timer_irq);
|
|
|
|
cns3xxx_clockevents_init(timer_irq);
|
|
}
|
|
|
|
void __init cns3xxx_timer_init(void)
|
|
{
|
|
cns3xxx_tmr1 = IOMEM(CNS3XXX_TIMER1_2_3_BASE_VIRT);
|
|
|
|
__cns3xxx_timer_init(IRQ_CNS3XXX_TIMER0);
|
|
}
|
|
|
|
#ifdef CONFIG_CACHE_L2X0
|
|
|
|
void __init cns3xxx_l2x0_init(void)
|
|
{
|
|
void __iomem *base = ioremap(CNS3XXX_L2C_BASE, SZ_4K);
|
|
u32 val;
|
|
|
|
if (WARN_ON(!base))
|
|
return;
|
|
|
|
/*
|
|
* Tag RAM Control register
|
|
*
|
|
* bit[10:8] - 1 cycle of write accesses latency
|
|
* bit[6:4] - 1 cycle of read accesses latency
|
|
* bit[3:0] - 1 cycle of setup latency
|
|
*
|
|
* 1 cycle of latency for setup, read and write accesses
|
|
*/
|
|
val = readl(base + L2X0_TAG_LATENCY_CTRL);
|
|
val &= 0xfffff888;
|
|
writel(val, base + L2X0_TAG_LATENCY_CTRL);
|
|
|
|
/*
|
|
* Data RAM Control register
|
|
*
|
|
* bit[10:8] - 1 cycles of write accesses latency
|
|
* bit[6:4] - 1 cycles of read accesses latency
|
|
* bit[3:0] - 1 cycle of setup latency
|
|
*
|
|
* 1 cycle of latency for setup, read and write accesses
|
|
*/
|
|
val = readl(base + L2X0_DATA_LATENCY_CTRL);
|
|
val &= 0xfffff888;
|
|
writel(val, base + L2X0_DATA_LATENCY_CTRL);
|
|
|
|
/* 32 KiB, 8-way, parity disable */
|
|
l2x0_init(base, 0x00540000, 0xfe000fff);
|
|
}
|
|
|
|
#endif /* CONFIG_CACHE_L2X0 */
|
|
|
|
static int csn3xxx_usb_power_on(struct platform_device *pdev)
|
|
{
|
|
/*
|
|
* EHCI and OHCI share the same clock and power,
|
|
* resetting twice would cause the 1st controller been reset.
|
|
* Therefore only do power up at the first up device, and
|
|
* power down at the last down device.
|
|
*
|
|
* Set USB AHB INCR length to 16
|
|
*/
|
|
if (atomic_inc_return(&usb_pwr_ref) == 1) {
|
|
cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB);
|
|
cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
|
|
cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST);
|
|
__raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)),
|
|
MISC_CHIP_CONFIG_REG);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void csn3xxx_usb_power_off(struct platform_device *pdev)
|
|
{
|
|
/*
|
|
* EHCI and OHCI share the same clock and power,
|
|
* resetting twice would cause the 1st controller been reset.
|
|
* Therefore only do power up at the first up device, and
|
|
* power down at the last down device.
|
|
*/
|
|
if (atomic_dec_return(&usb_pwr_ref) == 0)
|
|
cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
|
|
}
|
|
|
|
static struct usb_ehci_pdata cns3xxx_usb_ehci_pdata = {
|
|
.power_on = csn3xxx_usb_power_on,
|
|
.power_off = csn3xxx_usb_power_off,
|
|
};
|
|
|
|
static struct usb_ohci_pdata cns3xxx_usb_ohci_pdata = {
|
|
.num_ports = 1,
|
|
.power_on = csn3xxx_usb_power_on,
|
|
.power_off = csn3xxx_usb_power_off,
|
|
};
|
|
|
|
static struct of_dev_auxdata cns3xxx_auxdata[] __initconst = {
|
|
{ "intel,usb-ehci", CNS3XXX_USB_BASE, "ehci-platform", &cns3xxx_usb_ehci_pdata },
|
|
{ "intel,usb-ohci", CNS3XXX_USB_OHCI_BASE, "ohci-platform", &cns3xxx_usb_ohci_pdata },
|
|
{ "cavium,cns3420-ahci", CNS3XXX_SATA2_BASE, "ahci", NULL },
|
|
{ "cavium,cns3420-sdhci", CNS3XXX_SDIO_BASE, "ahci", NULL },
|
|
{},
|
|
};
|
|
|
|
static void __init cns3xxx_init(void)
|
|
{
|
|
struct device_node *dn;
|
|
|
|
cns3xxx_l2x0_init();
|
|
|
|
dn = of_find_compatible_node(NULL, NULL, "cavium,cns3420-ahci");
|
|
if (of_device_is_available(dn)) {
|
|
u32 tmp;
|
|
|
|
tmp = __raw_readl(MISC_SATA_POWER_MODE);
|
|
tmp |= 0x1 << 16; /* Disable SATA PHY 0 from SLUMBER Mode */
|
|
tmp |= 0x1 << 17; /* Disable SATA PHY 1 from SLUMBER Mode */
|
|
__raw_writel(tmp, MISC_SATA_POWER_MODE);
|
|
|
|
/* Enable SATA PHY */
|
|
cns3xxx_pwr_power_up(0x1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_SATA_PHY0);
|
|
cns3xxx_pwr_power_up(0x1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_SATA_PHY1);
|
|
|
|
/* Enable SATA Clock */
|
|
cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_SATA);
|
|
|
|
/* De-Asscer SATA Reset */
|
|
cns3xxx_pwr_soft_rst(CNS3XXX_PWR_SOFTWARE_RST(SATA));
|
|
}
|
|
|
|
dn = of_find_compatible_node(NULL, NULL, "cavium,cns3420-sdhci");
|
|
if (of_device_is_available(dn)) {
|
|
u32 __iomem *gpioa = IOMEM(CNS3XXX_MISC_BASE_VIRT + 0x0014);
|
|
u32 gpioa_pins = __raw_readl(gpioa);
|
|
|
|
/* MMC/SD pins share with GPIOA */
|
|
gpioa_pins |= 0x1fff0004;
|
|
__raw_writel(gpioa_pins, gpioa);
|
|
|
|
cns3xxx_pwr_clk_en(CNS3XXX_PWR_CLK_EN(SDIO));
|
|
cns3xxx_pwr_soft_rst(CNS3XXX_PWR_SOFTWARE_RST(SDIO));
|
|
}
|
|
|
|
pm_power_off = cns3xxx_power_off;
|
|
|
|
of_platform_populate(NULL, of_default_bus_match_table,
|
|
cns3xxx_auxdata, NULL);
|
|
}
|
|
|
|
static const char *cns3xxx_dt_compat[] __initdata = {
|
|
"cavium,cns3410",
|
|
"cavium,cns3420",
|
|
NULL,
|
|
};
|
|
|
|
DT_MACHINE_START(CNS3XXX_DT, "Cavium Networks CNS3xxx")
|
|
.dt_compat = cns3xxx_dt_compat,
|
|
.map_io = cns3xxx_map_io,
|
|
.init_irq = cns3xxx_init_irq,
|
|
.init_time = cns3xxx_timer_init,
|
|
.init_machine = cns3xxx_init,
|
|
.restart = cns3xxx_restart,
|
|
MACHINE_END
|