blob: 80939cee0f0b11d7ac247cd367902545234c0064 [file] [log] [blame]
/*
* linux/arch/arm64/mach/mmpx-dt.c
*
* Copyright (C) 2012 Marvell Technology Group Ltd.
* Author: Dongjiu Geng <djgeng@marvell.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
* publishhed by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/mmp.h>
#include <linux/of_platform.h>
#include <linux/clocksource.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/memblock.h>
#include <asm/mach/arch.h>
#include <asm/mcpm.h>
#include "regs-addr.h"
#include "mmpx-dt.h"
#include "soc_camera_dkb.h"
#ifdef CONFIG_SD8XXX_RFKILL
#include <linux/sd8x_rfkill.h>
#endif
#ifdef CONFIG_LEDS_REGULATOR
static struct platform_device keypad_backlight = {
.name = "button-backlight",
};
#endif
unsigned int mmp_chip_id;
EXPORT_SYMBOL(mmp_chip_id);
static const struct of_dev_auxdata helanx_auxdata_lookup[] __initconst = {
OF_DEV_AUXDATA("mrvl,mmp-sspa-dai", 0xd128dc00, "mmp-sspa-dai.0", NULL),
OF_DEV_AUXDATA("mrvl,mmp-sspa-dai", 0xd128dd00, "mmp-sspa-dai.1", NULL),
OF_DEV_AUXDATA("marvell,map-card", 0, "sound", NULL),
#ifdef CONFIG_SOC_CAMERA_S5K8AA
OF_DEV_AUXDATA("soc-camera-pdrv", 0, "soc-camera-pdrv.0", &soc_camera_desc_0),
#endif
#ifdef CONFIG_SOC_CAMERA_SR200
OF_DEV_AUXDATA("soc-camera-pdrv", 1, "soc-camera-pdrv.1", &soc_camera_desc_1),
#endif
#ifdef CONFIG_SOC_CAMERA_SP0A20_ECS
OF_DEV_AUXDATA("soc-camera-pdrv", 2, "soc-camera-pdrv.2",
&soc_camera_desc_2),
#endif
#ifdef CONFIG_SOC_CAMERA_SP2529_ECS
OF_DEV_AUXDATA("soc-camera-pdrv", 3, "soc-camera-pdrv.3",
&soc_camera_desc_4),
#endif
OF_DEV_AUXDATA("marvell,mmp-disp", 0xd420b000, "mmp-disp", NULL),
#ifdef CONFIG_SD8XXX_RFKILL
OF_DEV_AUXDATA("mrvl,sd8x-rfkill", 0, "sd8x-rfkill", NULL),
#endif
OF_DEV_AUXDATA("marvell,pxa27x-keypad", 0xd4012000, "pxa27x-keypad", NULL),
#ifdef CONFIG_LEDS_REGULATOR
OF_DEV_AUXDATA("regulator-leds", 0, "leds-regulator",
&keypad_backlight),
#endif
#ifdef CONFIG_SPI_PXA2XX
OF_DEV_AUXDATA("marvell,pxa910-spi", 0xd401c000, "pxa910-ssp.2", NULL),
#endif
{}
};
#define MPMU_PHYS_BASE 0xd4050000
#define MPMU_APRR (0x1020)
#define MPMU_WDTPCR (0x0200)
/* wdt and cp use the clock */
static __init void enable_pxawdt_clock(void)
{
void __iomem *mpmu_base;
mpmu_base = ioremap(MPMU_PHYS_BASE, SZ_8K);
if (!mpmu_base) {
pr_err("ioremap mpmu_base failed\n");
return;
}
/* reset/enable WDT clock */
writel(0x7, mpmu_base + MPMU_WDTPCR);
readl(mpmu_base + MPMU_WDTPCR);
writel(0x3, mpmu_base + MPMU_WDTPCR);
iounmap(mpmu_base);
}
#define GENERIC_COUNTER_PHYS_BASE 0xd4101000
#define CNTCR 0x00 /* Counter Control Register */
#define CNTCR_EN (1 << 0) /* The counter is enabled */
#define CNTCR_HDBG (1 << 1) /* Halt on debug */
#define APBC_PHY_BASE 0xd4015000
#define APBC_COUNTER_CLK_SEL 0x64
#define FREQ_HW_CTRL 0x1
static __init void enable_arch_timer(void)
{
void __iomem *apbc_base, *tmr_base;
u32 tmp;
apbc_base = ioremap(APBC_PHY_BASE, SZ_4K);
if (!apbc_base) {
pr_err("ioremap apbc_base failed\n");
return;
}
tmr_base = ioremap(GENERIC_COUNTER_PHYS_BASE, SZ_4K);
if (!tmr_base) {
pr_err("opremap tmr_base failed\n");
iounmap(apbc_base);
return;
}
tmp = readl(apbc_base + APBC_COUNTER_CLK_SEL);
/* Default is 26M/32768 = 0x319 */
if ((tmp >> 16) != 0x319) {
pr_warn("Generic Counter step of Low Freq is not right\n");
iounmap(apbc_base);
iounmap(tmr_base);
return;
}
/*
* bit0 = 1: Generic Counter Frequency control by hardware VCTCXO_EN
* VCTCXO_EN = 1, Generic Counter Frequency is 26Mhz;
* VCTCXO_EN = 0, Generic Counter Frequency is 32KHz.
*/
writel(tmp | FREQ_HW_CTRL, apbc_base + APBC_COUNTER_CLK_SEL);
/*
* NOTE: can not read CNTCR before write, otherwise write will fail
* Halt on debug;
* start the counter
*/
writel(CNTCR_HDBG | CNTCR_EN, tmr_base + CNTCR);
iounmap(tmr_base);
iounmap(apbc_base);
}
/* Common APB clock register bit definitions */
#define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */
#define APBC_FNCLK (1 << 1) /* Functional Clock Enable */
#define APBC_RST (1 << 2) /* Reset Generation */
/* Functional Clock Selection Mask */
#define APBC_FNCLKSEL(x) (((x) & 0xf) << 4)
#define APBC_TIMER0 0x34
#define APBC_TIMER1 0x44
#define APBC_TIMER2 0x68
static u32 timer_clkreg[] = {
APBC_TIMER0, APBC_TIMER1, APBC_TIMER2
};
static __init void enable_soc_timer(void)
{
void __iomem *apbc_base;
struct device_node *np;
int tid;
apbc_base = ioremap(APBC_PHY_BASE, SZ_4K);
if (!apbc_base) {
pr_err("ioremap apbc_base failed\n");
return;
}
/* timer APMU clock initialization */
for_each_compatible_node(np, NULL, "marvell,mmp-timer") {
if (of_device_is_available(np) &&
!of_property_read_u32(np, "marvell,timer-id", &tid)) {
if (tid >= ARRAY_SIZE(timer_clkreg))
continue;
/* Select the configurable clock rate to be 3.25MHz */
writel(APBC_APBCLK | APBC_RST, apbc_base + timer_clkreg[tid]);
writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
apbc_base + timer_clkreg[tid]);
}
}
iounmap(apbc_base);
}
#define APMU_SDH0 0x54
static void __init pxa1908_sdhc_reset_all(void)
{
unsigned int reg_tmp;
void __iomem *apmu_base;
apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
if (!apmu_base) {
pr_err("failed to get apmu_base va\n");
return;
}
/* use bit0 to reset all 3 sdh controls */
reg_tmp = __raw_readl(apmu_base + APMU_SDH0);
__raw_writel(reg_tmp & (~1), apmu_base + APMU_SDH0);
udelay(10);
__raw_writel(reg_tmp | (1), apmu_base + APMU_SDH0);
}
#define PMU_USB_CLK_RES_CTRL 0x5c
static void __init pxa1908_usb_reset_all(void)
{
unsigned int reg_tmp;
void __iomem *apmu_base;
apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
if (!apmu_base) {
pr_err("failed to get apmu_base va\n");
return;
}
/* use bit0 and bit3 to reset all usb controls */
__raw_writel(0, apmu_base + PMU_USB_CLK_RES_CTRL);
udelay(10);
reg_tmp = __raw_readl(apmu_base + PMU_USB_CLK_RES_CTRL);
if (reg_tmp)
pr_err("failed to clear usb controller\n");
}
static void __init helanx_irq_init(void)
{
irqchip_init();
/* only for wake up */
mmp_of_wakeup_init();
}
void __init helanx_timer_init(void)
{
void __iomem *chip_id;
regs_addr_iomap();
/* this is early, initialize mmp_chip_id here */
chip_id = regs_addr_get_va(REGS_ADDR_CIU);
mmp_chip_id = readl_relaxed(chip_id);
enable_pxawdt_clock();
#ifdef CONFIG_ARM_ARCH_TIMER
enable_arch_timer();
#endif
enable_soc_timer();
#ifdef CONFIG_ARM
of_clk_init(NULL);
#endif
clocksource_of_init();
pxa1908_usb_reset_all();
pxa1908_sdhc_reset_all();
}
void __init helanx_init_machine(void)
{
of_platform_populate(NULL, of_default_bus_match_table,
helanx_auxdata_lookup, &platform_bus);
}
#ifdef CONFIG_ARM
bool __init helanx_smp_init_ops(void)
{
mcpm_smp_set_ops();
return true;
}
#endif
static u32 cp_area_size = 0x02000000;
static u32 cp_area_addr = 0x06000000;
static int __init early_cpmem(char *p)
{
char *endp;
cp_area_size = memparse(p, &endp);
if (*endp == '@')
cp_area_addr = memparse(endp + 1, NULL);
return 0;
}
early_param("cpmem", early_cpmem);
static void pxa_reserve_cp_memblock(void)
{
/* Reserve memory for CP */
if (cp_area_size) {
/* panic when CP memory is larger than 64MB */
BUG_ON(cp_area_size > (1 << 26));
BUG_ON(memblock_reserve(cp_area_addr, cp_area_size) != 0);
pr_info("Reserved CP memory: 0x%x@0x%08x\n", cp_area_size, cp_area_addr);
}
}
static void __init helanx_reserve(void)
{
pxa_reserve_cp_memblock();
#ifdef CONFIG_MRVL_LOG
pxa_reserve_logmem();
#endif
}
static const char * const pxa1908_dt_board_compat[] __initconst = {
"marvell,pxa1908",
NULL,
};
DT_MACHINE_START(PXA1908_DT, "PXA1908")
#ifdef CONFIG_ARM
.smp_init = smp_init_ops(helanx_smp_init_ops),
#endif
.init_time = helanx_timer_init,
.init_irq = helanx_irq_init,
.init_machine = helanx_init_machine,
.dt_compat = pxa1908_dt_board_compat,
.reserve = helanx_reserve,
MACHINE_END
static const char * const pxa1936_dt_board_compat[] __initconst = {
"marvell,pxa1936",
NULL,
};
DT_MACHINE_START(PXA1936_DT, "PXA1936")
.init_time = helanx_timer_init,
.init_irq = helanx_irq_init,
.init_machine = helanx_init_machine,
.dt_compat = pxa1936_dt_board_compat,
.reserve = helanx_reserve,
MACHINE_END