forked from Minki/linux
ad09cb8381
Add r8a7790 SMP support using the shared APMU code. To enable SMP the r8a7790 specific DTS needs to be updated to include CPU cores, and this is happening in a separate patch. Signed-off-by: Magnus Damm <damm@opensource.se> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
292 lines
8.1 KiB
C
292 lines
8.1 KiB
C
/*
|
|
* r8a7790 processor support
|
|
*
|
|
* Copyright (C) 2013 Renesas Solutions Corp.
|
|
* Copyright (C) 2013 Magnus Damm
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <linux/clocksource.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_data/gpio-rcar.h>
|
|
#include <linux/platform_data/irq-renesas-irqc.h>
|
|
#include <linux/serial_sci.h>
|
|
#include <linux/sh_timer.h>
|
|
#include <mach/common.h>
|
|
#include <mach/irqs.h>
|
|
#include <mach/r8a7790.h>
|
|
#include <asm/mach/arch.h>
|
|
|
|
static const struct resource pfc_resources[] __initconst = {
|
|
DEFINE_RES_MEM(0xe6060000, 0x250),
|
|
};
|
|
|
|
#define R8A7790_GPIO(idx) \
|
|
static const struct resource r8a7790_gpio##idx##_resources[] __initconst = { \
|
|
DEFINE_RES_MEM(0xe6050000 + 0x1000 * (idx), 0x50), \
|
|
DEFINE_RES_IRQ(gic_spi(4 + (idx))), \
|
|
}; \
|
|
\
|
|
static const struct gpio_rcar_config \
|
|
r8a7790_gpio##idx##_platform_data __initconst = { \
|
|
.gpio_base = 32 * (idx), \
|
|
.irq_base = 0, \
|
|
.number_of_pins = 32, \
|
|
.pctl_name = "pfc-r8a7790", \
|
|
.has_both_edge_trigger = 1, \
|
|
}; \
|
|
|
|
R8A7790_GPIO(0);
|
|
R8A7790_GPIO(1);
|
|
R8A7790_GPIO(2);
|
|
R8A7790_GPIO(3);
|
|
R8A7790_GPIO(4);
|
|
R8A7790_GPIO(5);
|
|
|
|
#define r8a7790_register_gpio(idx) \
|
|
platform_device_register_resndata(&platform_bus, "gpio_rcar", idx, \
|
|
r8a7790_gpio##idx##_resources, \
|
|
ARRAY_SIZE(r8a7790_gpio##idx##_resources), \
|
|
&r8a7790_gpio##idx##_platform_data, \
|
|
sizeof(r8a7790_gpio##idx##_platform_data))
|
|
|
|
void __init r8a7790_pinmux_init(void)
|
|
{
|
|
platform_device_register_simple("pfc-r8a7790", -1, pfc_resources,
|
|
ARRAY_SIZE(pfc_resources));
|
|
r8a7790_register_gpio(0);
|
|
r8a7790_register_gpio(1);
|
|
r8a7790_register_gpio(2);
|
|
r8a7790_register_gpio(3);
|
|
r8a7790_register_gpio(4);
|
|
r8a7790_register_gpio(5);
|
|
}
|
|
|
|
#define SCIF_COMMON(scif_type, baseaddr, irq) \
|
|
.type = scif_type, \
|
|
.mapbase = baseaddr, \
|
|
.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \
|
|
.irqs = SCIx_IRQ_MUXED(irq)
|
|
|
|
#define SCIFA_DATA(index, baseaddr, irq) \
|
|
[index] = { \
|
|
SCIF_COMMON(PORT_SCIFA, baseaddr, irq), \
|
|
.scbrr_algo_id = SCBRR_ALGO_4, \
|
|
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE0, \
|
|
}
|
|
|
|
#define SCIFB_DATA(index, baseaddr, irq) \
|
|
[index] = { \
|
|
SCIF_COMMON(PORT_SCIFB, baseaddr, irq), \
|
|
.scbrr_algo_id = SCBRR_ALGO_4, \
|
|
.scscr = SCSCR_RE | SCSCR_TE, \
|
|
}
|
|
|
|
#define SCIF_DATA(index, baseaddr, irq) \
|
|
[index] = { \
|
|
SCIF_COMMON(PORT_SCIF, baseaddr, irq), \
|
|
.scbrr_algo_id = SCBRR_ALGO_2, \
|
|
.scscr = SCSCR_RE | SCSCR_TE, \
|
|
}
|
|
|
|
#define HSCIF_DATA(index, baseaddr, irq) \
|
|
[index] = { \
|
|
SCIF_COMMON(PORT_HSCIF, baseaddr, irq), \
|
|
.scbrr_algo_id = SCBRR_ALGO_6, \
|
|
.scscr = SCSCR_RE | SCSCR_TE, \
|
|
}
|
|
|
|
enum { SCIFA0, SCIFA1, SCIFB0, SCIFB1, SCIFB2, SCIFA2, SCIF0, SCIF1,
|
|
HSCIF0, HSCIF1 };
|
|
|
|
static const struct plat_sci_port scif[] __initconst = {
|
|
SCIFA_DATA(SCIFA0, 0xe6c40000, gic_spi(144)), /* SCIFA0 */
|
|
SCIFA_DATA(SCIFA1, 0xe6c50000, gic_spi(145)), /* SCIFA1 */
|
|
SCIFB_DATA(SCIFB0, 0xe6c20000, gic_spi(148)), /* SCIFB0 */
|
|
SCIFB_DATA(SCIFB1, 0xe6c30000, gic_spi(149)), /* SCIFB1 */
|
|
SCIFB_DATA(SCIFB2, 0xe6ce0000, gic_spi(150)), /* SCIFB2 */
|
|
SCIFA_DATA(SCIFA2, 0xe6c60000, gic_spi(151)), /* SCIFA2 */
|
|
SCIF_DATA(SCIF0, 0xe6e60000, gic_spi(152)), /* SCIF0 */
|
|
SCIF_DATA(SCIF1, 0xe6e68000, gic_spi(153)), /* SCIF1 */
|
|
HSCIF_DATA(HSCIF0, 0xe62c0000, gic_spi(154)), /* HSCIF0 */
|
|
HSCIF_DATA(HSCIF1, 0xe62c8000, gic_spi(155)), /* HSCIF1 */
|
|
};
|
|
|
|
static inline void r8a7790_register_scif(int idx)
|
|
{
|
|
platform_device_register_data(&platform_bus, "sh-sci", idx, &scif[idx],
|
|
sizeof(struct plat_sci_port));
|
|
}
|
|
|
|
static const struct renesas_irqc_config irqc0_data __initconst = {
|
|
.irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */
|
|
};
|
|
|
|
static const struct resource irqc0_resources[] __initconst = {
|
|
DEFINE_RES_MEM(0xe61c0000, 0x200), /* IRQC Event Detector Block_0 */
|
|
DEFINE_RES_IRQ(gic_spi(0)), /* IRQ0 */
|
|
DEFINE_RES_IRQ(gic_spi(1)), /* IRQ1 */
|
|
DEFINE_RES_IRQ(gic_spi(2)), /* IRQ2 */
|
|
DEFINE_RES_IRQ(gic_spi(3)), /* IRQ3 */
|
|
};
|
|
|
|
#define r8a7790_register_irqc(idx) \
|
|
platform_device_register_resndata(&platform_bus, "renesas_irqc", \
|
|
idx, irqc##idx##_resources, \
|
|
ARRAY_SIZE(irqc##idx##_resources), \
|
|
&irqc##idx##_data, \
|
|
sizeof(struct renesas_irqc_config))
|
|
|
|
static const struct resource thermal_resources[] __initconst = {
|
|
DEFINE_RES_MEM(0xe61f0000, 0x14),
|
|
DEFINE_RES_MEM(0xe61f0100, 0x38),
|
|
DEFINE_RES_IRQ(gic_spi(69)),
|
|
};
|
|
|
|
#define r8a7790_register_thermal() \
|
|
platform_device_register_simple("rcar_thermal", -1, \
|
|
thermal_resources, \
|
|
ARRAY_SIZE(thermal_resources))
|
|
|
|
static const struct sh_timer_config cmt00_platform_data __initconst = {
|
|
.name = "CMT00",
|
|
.timer_bit = 0,
|
|
.clockevent_rating = 80,
|
|
};
|
|
|
|
static const struct resource cmt00_resources[] __initconst = {
|
|
DEFINE_RES_MEM(0xffca0510, 0x0c),
|
|
DEFINE_RES_MEM(0xffca0500, 0x04),
|
|
DEFINE_RES_IRQ(gic_spi(142)), /* CMT0_0 */
|
|
};
|
|
|
|
#define r8a7790_register_cmt(idx) \
|
|
platform_device_register_resndata(&platform_bus, "sh_cmt", \
|
|
idx, cmt##idx##_resources, \
|
|
ARRAY_SIZE(cmt##idx##_resources), \
|
|
&cmt##idx##_platform_data, \
|
|
sizeof(struct sh_timer_config))
|
|
|
|
void __init r8a7790_add_dt_devices(void)
|
|
{
|
|
r8a7790_register_scif(SCIFA0);
|
|
r8a7790_register_scif(SCIFA1);
|
|
r8a7790_register_scif(SCIFB0);
|
|
r8a7790_register_scif(SCIFB1);
|
|
r8a7790_register_scif(SCIFB2);
|
|
r8a7790_register_scif(SCIFA2);
|
|
r8a7790_register_scif(SCIF0);
|
|
r8a7790_register_scif(SCIF1);
|
|
r8a7790_register_scif(HSCIF0);
|
|
r8a7790_register_scif(HSCIF1);
|
|
r8a7790_register_cmt(00);
|
|
}
|
|
|
|
void __init r8a7790_add_standard_devices(void)
|
|
{
|
|
r8a7790_add_dt_devices();
|
|
r8a7790_register_irqc(0);
|
|
r8a7790_register_thermal();
|
|
}
|
|
|
|
#define MODEMR 0xe6160060
|
|
|
|
u32 __init r8a7790_read_mode_pins(void)
|
|
{
|
|
void __iomem *modemr = ioremap_nocache(MODEMR, 4);
|
|
u32 mode;
|
|
|
|
BUG_ON(!modemr);
|
|
mode = ioread32(modemr);
|
|
iounmap(modemr);
|
|
|
|
return mode;
|
|
}
|
|
|
|
#define CNTCR 0
|
|
#define CNTFID0 0x20
|
|
|
|
void __init r8a7790_timer_init(void)
|
|
{
|
|
#ifdef CONFIG_ARM_ARCH_TIMER
|
|
u32 mode = r8a7790_read_mode_pins();
|
|
void __iomem *base;
|
|
int extal_mhz = 0;
|
|
u32 freq;
|
|
|
|
/* At Linux boot time the r8a7790 arch timer comes up
|
|
* with the counter disabled. Moreover, it may also report
|
|
* a potentially incorrect fixed 13 MHz frequency. To be
|
|
* correct these registers need to be updated to use the
|
|
* frequency EXTAL / 2 which can be determined by the MD pins.
|
|
*/
|
|
|
|
switch (mode & (MD(14) | MD(13))) {
|
|
case 0:
|
|
extal_mhz = 15;
|
|
break;
|
|
case MD(13):
|
|
extal_mhz = 20;
|
|
break;
|
|
case MD(14):
|
|
extal_mhz = 26;
|
|
break;
|
|
case MD(13) | MD(14):
|
|
extal_mhz = 30;
|
|
break;
|
|
}
|
|
|
|
/* The arch timer frequency equals EXTAL / 2 */
|
|
freq = extal_mhz * (1000000 / 2);
|
|
|
|
/* Remap "armgcnt address map" space */
|
|
base = ioremap(0xe6080000, PAGE_SIZE);
|
|
|
|
/* Update registers with correct frequency */
|
|
iowrite32(freq, base + CNTFID0);
|
|
asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq));
|
|
|
|
/* make sure arch timer is started by setting bit 0 of CNTCR */
|
|
iowrite32(1, base + CNTCR);
|
|
iounmap(base);
|
|
#endif /* CONFIG_ARM_ARCH_TIMER */
|
|
|
|
clocksource_of_init();
|
|
}
|
|
|
|
void __init r8a7790_init_early(void)
|
|
{
|
|
#ifndef CONFIG_ARM_ARCH_TIMER
|
|
shmobile_setup_delay(1300, 2, 4); /* Cortex-A15 @ 1300MHz */
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_USE_OF
|
|
|
|
static const char * const r8a7790_boards_compat_dt[] __initconst = {
|
|
"renesas,r8a7790",
|
|
NULL,
|
|
};
|
|
|
|
DT_MACHINE_START(R8A7790_DT, "Generic R8A7790 (Flattened Device Tree)")
|
|
.smp = smp_ops(r8a7790_smp_ops),
|
|
.init_early = r8a7790_init_early,
|
|
.init_time = r8a7790_timer_init,
|
|
.dt_compat = r8a7790_boards_compat_dt,
|
|
MACHINE_END
|
|
#endif /* CONFIG_USE_OF */
|