diff options
Diffstat (limited to 'arch/arm/mach-shmobile')
67 files changed, 13438 insertions, 0 deletions
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig new file mode 100644 index 000000000..0fb484221 --- /dev/null +++ b/arch/arm/mach-shmobile/Kconfig @@ -0,0 +1,213 @@ +config ARCH_SHMOBILE + bool + select ZONE_DMA if ARM_LPAE + +config PM_RCAR + bool + +config PM_RMOBILE + bool + select PM_GENERIC_DOMAINS + +config ARCH_RCAR_GEN1 + bool + select PM_RCAR if PM || SMP + select RENESAS_INTC_IRQPIN + select SYS_SUPPORTS_SH_TMU + +config ARCH_RCAR_GEN2 + bool + select PM_RCAR if PM || SMP + select RENESAS_IRQC + select SYS_SUPPORTS_SH_CMT + select PCI_DOMAINS if PCI + +config ARCH_RMOBILE + bool + select PM_RMOBILE if PM + select SYS_SUPPORTS_SH_CMT + select SYS_SUPPORTS_SH_TMU + +menuconfig ARCH_SHMOBILE_MULTI + bool "Renesas ARM SoCs" if ARCH_MULTI_V7 + depends on MMU + select ARCH_SHMOBILE + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if SMP + select ARM_GIC + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE + select NO_IOPORT_MAP + select PINCTRL + select ARCH_REQUIRE_GPIOLIB + +if ARCH_SHMOBILE_MULTI + +#comment "Renesas ARM SoCs System Type" + +config ARCH_EMEV2 + bool "Emma Mobile EV2" + select SYS_SUPPORTS_EM_STI + +config ARCH_R7S72100 + bool "RZ/A1H (R7S72100)" + select SYS_SUPPORTS_SH_MTU2 + +config ARCH_R8A73A4 + bool "R-Mobile APE6 (R8A73A40)" + select ARCH_RMOBILE + select RENESAS_IRQC + +config ARCH_R8A7740 + bool "R-Mobile A1 (R8A77400)" + select ARCH_RMOBILE + select RENESAS_INTC_IRQPIN + +config ARCH_R8A7778 + bool "R-Car M1A (R8A77781)" + select ARCH_RCAR_GEN1 + +config ARCH_R8A7779 + bool "R-Car H1 (R8A77790)" + select ARCH_RCAR_GEN1 + +config ARCH_R8A7790 + bool "R-Car H2 (R8A77900)" + select ARCH_RCAR_GEN2 + select I2C + +config ARCH_R8A7791 + bool "R-Car M2-W (R8A77910)" + select ARCH_RCAR_GEN2 + select I2C + +config ARCH_R8A7794 + bool "R-Car E2 (R8A77940)" + select ARCH_RCAR_GEN2 + +config ARCH_SH73A0 + bool "SH-Mobile AG5 (R8A73A00)" + select ARCH_RMOBILE + select RENESAS_INTC_IRQPIN + +comment "Renesas ARM SoCs Board Type" + +config MACH_MARZEN + bool "MARZEN board" + depends on ARCH_R8A7779 + select REGULATOR_FIXED_VOLTAGE if REGULATOR + +comment "Renesas ARM SoCs System Configuration" +endif + +if ARCH_SHMOBILE_LEGACY + +comment "Renesas ARM SoCs System Type" + +config ARCH_SH73A0 + bool "SH-Mobile AG5 (R8A73A00)" + select ARCH_RMOBILE + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_GIC + select I2C + select SH_INTC + select RENESAS_INTC_IRQPIN + +config ARCH_R8A7740 + bool "R-Mobile A1 (R8A77400)" + select ARCH_RMOBILE + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_GIC + select RENESAS_INTC_IRQPIN + +config ARCH_R8A7778 + bool "R-Car M1A (R8A77781)" + select ARCH_RCAR_GEN1 + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_GIC + +config ARCH_R8A7779 + bool "R-Car H1 (R8A77790)" + select ARCH_RCAR_GEN1 + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_GIC + +comment "Renesas ARM SoCs Board Type" + +config MACH_ARMADILLO800EVA + bool "Armadillo-800 EVA board" + depends on ARCH_R8A7740 + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select SMSC_PHY if SH_ETH + select SND_SOC_WM8978 if SND_SIMPLE_CARD + select USE_OF + +config MACH_BOCKW + bool "BOCK-W platform" + depends on ARCH_R8A7778 + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select SND_SOC_AK4554 if SND_SIMPLE_CARD + select SND_SOC_AK4642 if SND_SIMPLE_CARD + select USE_OF + +config MACH_BOCKW_REFERENCE + bool "BOCK-W - Reference Device Tree Implementation" + depends on ARCH_R8A7778 + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select USE_OF + ---help--- + Use reference implementation of BockW board support + which makes use of device tree at the expense + of not supporting a number of devices. + + This is intended to aid developers + +config MACH_MARZEN + bool "MARZEN board" + depends on ARCH_R8A7779 + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select USE_OF + +config MACH_KZM9G + bool "KZM-A9-GT board" + depends on ARCH_SH73A0 + select ARCH_REQUIRE_GPIOLIB + select REGULATOR_FIXED_VOLTAGE if REGULATOR + select SND_SOC_AK4642 if SND_SIMPLE_CARD + select USE_OF + +comment "Renesas ARM SoCs System Configuration" + +config CPU_HAS_INTEVT + bool + default y + +config SH_CLK_CPG + bool + +source "drivers/sh/Kconfig" + +endif + +if ARCH_SHMOBILE + +menu "Timer and clock configuration" + +config SHMOBILE_TIMER_HZ + int "Kernel HZ (jiffies per second)" + range 32 1024 + default "128" + help + Allows the configuration of the timer frequency. It is customary + to have the timer interrupt run at 1000 Hz or 100 Hz, but in the + case of low timer frequencies other values may be more suitable. + Renesas ARM SoC systems using a 32768 Hz RCLK for clock events may + want to select a HZ value such as 128 that can evenly divide RCLK. + A HZ value that does not divide evenly may cause timer drift. + +endmenu + +endif diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile new file mode 100644 index 000000000..89e463de4 --- /dev/null +++ b/arch/arm/mach-shmobile/Makefile @@ -0,0 +1,65 @@ +# +# Makefile for the linux kernel. +# + +# Common objects +obj-y := timer.o console.o + +# CPU objects +obj-$(CONFIG_ARCH_SH73A0) += setup-sh73a0.o pm-sh73a0.o +obj-$(CONFIG_ARCH_R8A73A4) += setup-r8a73a4.o +obj-$(CONFIG_ARCH_R8A7740) += setup-r8a7740.o pm-r8a7740.o +obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o +obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o pm-r8a7779.o +obj-$(CONFIG_ARCH_R8A7790) += setup-r8a7790.o +obj-$(CONFIG_ARCH_R8A7791) += setup-r8a7791.o +obj-$(CONFIG_ARCH_R8A7794) += setup-r8a7794.o +obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o +obj-$(CONFIG_ARCH_R7S72100) += setup-r7s72100.o + +# Clock objects +ifndef CONFIG_COMMON_CLK +obj-y += clock.o +obj-$(CONFIG_ARCH_SH73A0) += clock-sh73a0.o +obj-$(CONFIG_ARCH_R8A7740) += clock-r8a7740.o +obj-$(CONFIG_ARCH_R8A7778) += clock-r8a7778.o +obj-$(CONFIG_ARCH_R8A7779) += clock-r8a7779.o +endif + +# CPU reset vector handling objects +cpu-y := platsmp.o headsmp.o + +# Shared SoC family objects +obj-$(CONFIG_ARCH_RCAR_GEN2) += setup-rcar-gen2.o platsmp-apmu.o $(cpu-y) +CFLAGS_setup-rcar-gen2.o += -march=armv7-a +obj-$(CONFIG_ARCH_R8A7790) += regulator-quirk-rcar-gen2.o +obj-$(CONFIG_ARCH_R8A7791) += regulator-quirk-rcar-gen2.o + +# SMP objects +smp-y := $(cpu-y) +smp-$(CONFIG_ARCH_SH73A0) += smp-sh73a0.o headsmp-scu.o platsmp-scu.o +smp-$(CONFIG_ARCH_R8A7779) += smp-r8a7779.o headsmp-scu.o platsmp-scu.o +smp-$(CONFIG_ARCH_R8A7790) += smp-r8a7790.o +smp-$(CONFIG_ARCH_R8A7791) += smp-r8a7791.o +smp-$(CONFIG_ARCH_EMEV2) += smp-emev2.o headsmp-scu.o platsmp-scu.o + +# PM objects +obj-$(CONFIG_SUSPEND) += suspend.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o +obj-$(CONFIG_PM_RCAR) += pm-rcar.o +obj-$(CONFIG_PM_RMOBILE) += pm-rmobile.o +obj-$(CONFIG_ARCH_RCAR_GEN2) += pm-rcar-gen2.o + +# Board objects +ifdef CONFIG_ARCH_SHMOBILE_MULTI +obj-$(CONFIG_MACH_MARZEN) += board-marzen-reference.o +else +obj-$(CONFIG_MACH_BOCKW) += board-bockw.o +obj-$(CONFIG_MACH_BOCKW_REFERENCE) += board-bockw-reference.o +obj-$(CONFIG_MACH_MARZEN) += board-marzen.o +obj-$(CONFIG_MACH_ARMADILLO800EVA) += board-armadillo800eva.o +obj-$(CONFIG_MACH_KZM9G) += board-kzm9g.o intc-sh73a0.o +endif + +# Framework support +obj-$(CONFIG_SMP) += $(smp-y) diff --git a/arch/arm/mach-shmobile/Makefile.boot b/arch/arm/mach-shmobile/Makefile.boot new file mode 100644 index 000000000..e1ef19cef --- /dev/null +++ b/arch/arm/mach-shmobile/Makefile.boot @@ -0,0 +1,15 @@ +# per-board load address for uImage +loadaddr-y := +loadaddr-$(CONFIG_MACH_ARMADILLO800EVA) += 0x40008000 +loadaddr-$(CONFIG_MACH_BOCKW) += 0x60008000 +loadaddr-$(CONFIG_MACH_BOCKW_REFERENCE) += 0x60008000 +loadaddr-$(CONFIG_MACH_KZM9G) += 0x41008000 +loadaddr-$(CONFIG_MACH_MARZEN) += 0x60008000 + +__ZRELADDR := $(sort $(loadaddr-y)) + zreladdr-y += $(__ZRELADDR) + +# Unsupported legacy stuff +# +#params_phys-y (Instead: Pass atags pointer in r2) +#initrd_phys-y (Instead: Use compiled-in initramfs) diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c new file mode 100644 index 000000000..bf37e3c53 --- /dev/null +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -0,0 +1,1365 @@ +/* + * armadillo 800 eva board support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/i2c-gpio.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/mfd/tmio.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sh_mmcif.h> +#include <linux/mmc/sh_mobile_sdhi.h> +#include <linux/pinctrl/machine.h> +#include <linux/platform_data/st1232_pdata.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/pwm_backlight.h> +#include <linux/reboot.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/gpio-regulator.h> +#include <linux/regulator/machine.h> +#include <linux/sh_eth.h> +#include <linux/usb/renesas_usbhs.h> +#include <linux/videodev2.h> + +#include <asm/hardware/cache-l2x0.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/time.h> +#include <asm/page.h> +#include <media/mt9t112.h> +#include <media/sh_mobile_ceu.h> +#include <media/soc_camera.h> +#include <sound/sh_fsi.h> +#include <sound/simple_card.h> +#include <video/sh_mobile_hdmi.h> +#include <video/sh_mobile_lcdc.h> + +#include "common.h" +#include "irqs.h" +#include "pm-rmobile.h" +#include "r8a7740.h" +#include "sh-gpio.h" + +/* + * CON1 Camera Module + * CON2 Extension Bus + * CON3 HDMI Output + * CON4 Composite Video Output + * CON5 H-UDI JTAG + * CON6 ARM JTAG + * CON7 SD1 + * CON8 SD2 + * CON9 RTC BackUp + * CON10 Monaural Mic Input + * CON11 Stereo Headphone Output + * CON12 Audio Line Output(L) + * CON13 Audio Line Output(R) + * CON14 AWL13 Module + * CON15 Extension + * CON16 LCD1 + * CON17 LCD2 + * CON19 Power Input + * CON20 USB1 + * CON21 USB2 + * CON22 Serial + * CON23 LAN + * CON24 USB3 + * LED1 Camera LED(Yellow) + * LED2 Power LED (Green) + * ED3-LED6 User LED(Yellow) + * LED7 LAN link LED(Green) + * LED8 LAN activity LED(Yellow) + */ + +/* + * DipSwitch + * + * SW1 + * + * -12345678-+---------------+---------------------------- + * 1 | boot | hermit + * 0 | boot | OS auto boot + * -12345678-+---------------+---------------------------- + * 00 | boot device | eMMC + * 10 | boot device | SDHI0 (CON7) + * 01 | boot device | - + * 11 | boot device | Extension Buss (CS0) + * -12345678-+---------------+---------------------------- + * 0 | Extension Bus | D8-D15 disable, eMMC enable + * 1 | Extension Bus | D8-D15 enable, eMMC disable + * -12345678-+---------------+---------------------------- + * 0 | SDHI1 | COM8 disable, COM14 enable + * 1 | SDHI1 | COM8 enable, COM14 disable + * -12345678-+---------------+---------------------------- + * 0 | USB0 | COM20 enable, COM24 disable + * 1 | USB0 | COM20 disable, COM24 enable + * -12345678-+---------------+---------------------------- + * 00 | JTAG | SH-X2 + * 10 | JTAG | ARM + * 01 | JTAG | - + * 11 | JTAG | Boundary Scan + *-----------+---------------+---------------------------- + */ + +/* + * FSI-WM8978 + * + * this command is required when playback. + * + * # amixer set "Headphone" 50 + * + * this command is required when capture. + * + * # amixer set "Input PGA" 15 + * # amixer set "Left Input Mixer MicP" on + * # amixer set "Left Input Mixer MicN" on + * # amixer set "Right Input Mixer MicN" on + * # amixer set "Right Input Mixer MicP" on + */ + +/* + * USB function + * + * When you use USB Function, + * set SW1.6 ON, and connect cable to CN24. + * + * USBF needs workaround on R8A7740 chip. + * These are a little bit complex. + * see + * usbhsf_power_ctrl() + */ +#define IRQ7 irq_pin(7) +#define USBCR1 IOMEM(0xe605810a) +#define USBH 0xC6700000 +#define USBH_USBCTR 0x10834 + +struct usbhsf_private { + struct clk *phy; + struct clk *usb24; + struct clk *pci; + struct clk *func; + struct clk *host; + void __iomem *usbh_base; + struct renesas_usbhs_platform_info info; +}; + +#define usbhsf_get_priv(pdev) \ + container_of(renesas_usbhs_get_info(pdev), \ + struct usbhsf_private, info) + +static int usbhsf_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +static int usbhsf_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhsf_private *priv = usbhsf_get_priv(pdev); + + /* + * Work around for USB Function. + * It needs USB host clock, and settings + */ + if (enable) { + /* + * enable all the related usb clocks + * for usb workaround + */ + clk_enable(priv->usb24); + clk_enable(priv->pci); + clk_enable(priv->host); + clk_enable(priv->func); + clk_enable(priv->phy); + + /* + * set USBCR1 + * + * Port1 is driven by USB function, + * Port2 is driven by USB HOST + * One HOST (Port1 or Port2 is HOST) + * USB PLL input clock = 24MHz + */ + __raw_writew(0xd750, USBCR1); + mdelay(1); + + /* + * start USB Host + */ + __raw_writel(0x0000000c, priv->usbh_base + USBH_USBCTR); + __raw_writel(0x00000008, priv->usbh_base + USBH_USBCTR); + mdelay(10); + + /* + * USB PHY Power ON + */ + __raw_writew(0xd770, USBCR1); + __raw_writew(0x4000, base + 0x102); /* USBF :: SUSPMODE */ + + } else { + __raw_writel(0x0000010f, priv->usbh_base + USBH_USBCTR); + __raw_writew(0xd7c0, USBCR1); /* GPIO */ + + clk_disable(priv->phy); + clk_disable(priv->func); /* usb work around */ + clk_disable(priv->host); /* usb work around */ + clk_disable(priv->pci); /* usb work around */ + clk_disable(priv->usb24); /* usb work around */ + } + + return 0; +} + +static int usbhsf_get_vbus(struct platform_device *pdev) +{ + return gpio_get_value(209); +} + +static irqreturn_t usbhsf_interrupt(int irq, void *data) +{ + struct platform_device *pdev = data; + + renesas_usbhs_call_notify_hotplug(pdev); + + return IRQ_HANDLED; +} + +static int usbhsf_hardware_exit(struct platform_device *pdev) +{ + struct usbhsf_private *priv = usbhsf_get_priv(pdev); + + if (!IS_ERR(priv->phy)) + clk_put(priv->phy); + if (!IS_ERR(priv->usb24)) + clk_put(priv->usb24); + if (!IS_ERR(priv->pci)) + clk_put(priv->pci); + if (!IS_ERR(priv->host)) + clk_put(priv->host); + if (!IS_ERR(priv->func)) + clk_put(priv->func); + if (priv->usbh_base) + iounmap(priv->usbh_base); + + priv->phy = NULL; + priv->usb24 = NULL; + priv->pci = NULL; + priv->host = NULL; + priv->func = NULL; + priv->usbh_base = NULL; + + free_irq(IRQ7, pdev); + + return 0; +} + +static int usbhsf_hardware_init(struct platform_device *pdev) +{ + struct usbhsf_private *priv = usbhsf_get_priv(pdev); + int ret; + + priv->phy = clk_get(&pdev->dev, "phy"); + priv->usb24 = clk_get(&pdev->dev, "usb24"); + priv->pci = clk_get(&pdev->dev, "pci"); + priv->func = clk_get(&pdev->dev, "func"); + priv->host = clk_get(&pdev->dev, "host"); + priv->usbh_base = ioremap_nocache(USBH, 0x20000); + + if (IS_ERR(priv->phy) || + IS_ERR(priv->usb24) || + IS_ERR(priv->pci) || + IS_ERR(priv->host) || + IS_ERR(priv->func) || + !priv->usbh_base) { + dev_err(&pdev->dev, "USB clock setting failed\n"); + usbhsf_hardware_exit(pdev); + return -EIO; + } + + ret = request_irq(IRQ7, usbhsf_interrupt, IRQF_TRIGGER_NONE, + dev_name(&pdev->dev), pdev); + if (ret) { + dev_err(&pdev->dev, "request_irq err\n"); + return ret; + } + irq_set_irq_type(IRQ7, IRQ_TYPE_EDGE_BOTH); + + /* usb24 use 1/1 of parent clock (= usb24s = 24MHz) */ + clk_set_rate(priv->usb24, + clk_get_rate(clk_get_parent(priv->usb24))); + + return 0; +} + +static struct usbhsf_private usbhsf_private = { + .info = { + .platform_callback = { + .get_id = usbhsf_get_id, + .get_vbus = usbhsf_get_vbus, + .hardware_init = usbhsf_hardware_init, + .hardware_exit = usbhsf_hardware_exit, + .power_ctrl = usbhsf_power_ctrl, + }, + .driver_param = { + .buswait_bwait = 5, + .detection_delay = 5, + .d0_rx_id = SHDMA_SLAVE_USBHS_RX, + .d1_tx_id = SHDMA_SLAVE_USBHS_TX, + }, + } +}; + +static struct resource usbhsf_resources[] = { + { + .name = "USBHS", + .start = 0xe6890000, + .end = 0xe6890104 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = gic_spi(51), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbhsf_device = { + .name = "renesas_usbhs", + .dev = { + .platform_data = &usbhsf_private.info, + }, + .id = -1, + .num_resources = ARRAY_SIZE(usbhsf_resources), + .resource = usbhsf_resources, +}; + +/* Ether */ +static struct sh_eth_plat_data sh_eth_platdata = { + .phy = 0x00, /* LAN8710A */ + .edmac_endian = EDMAC_LITTLE_ENDIAN, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct resource sh_eth_resources[] = { + { + .start = 0xe9a00000, + .end = 0xe9a00800 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = 0xe9a01800, + .end = 0xe9a02000 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = gic_spi(110), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sh_eth_device = { + .name = "r8a7740-gether", + .id = -1, + .dev = { + .platform_data = &sh_eth_platdata, + .dma_mask = &sh_eth_device.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .resource = sh_eth_resources, + .num_resources = ARRAY_SIZE(sh_eth_resources), +}; + +/* PWM */ +static struct resource pwm_resources[] = { + [0] = { + .start = 0xe6600000, + .end = 0xe66000ff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device pwm_device = { + .name = "renesas-tpu-pwm", + .id = -1, + .num_resources = ARRAY_SIZE(pwm_resources), + .resource = pwm_resources, +}; + +static struct pwm_lookup pwm_lookup[] = { + PWM_LOOKUP("renesas-tpu-pwm", 2, "pwm-backlight.0", NULL, + 33333, PWM_POLARITY_INVERSED), +}; + +/* LCDC and backlight */ +static struct platform_pwm_backlight_data pwm_backlight_data = { + .lth_brightness = 50, + .max_brightness = 255, + .dft_brightness = 255, + .pwm_period_ns = 33333, /* 30kHz */ + .enable_gpio = 61, +}; + +static struct platform_device pwm_backlight_device = { + .name = "pwm-backlight", + .dev = { + .platform_data = &pwm_backlight_data, + }, +}; + +static struct fb_videomode lcdc0_mode = { + .name = "AMPIER/AM-800480", + .xres = 800, + .yres = 480, + .left_margin = 88, + .right_margin = 40, + .hsync_len = 128, + .upper_margin = 20, + .lower_margin = 5, + .vsync_len = 5, + .sync = 0, +}; + +static struct sh_mobile_lcdc_info lcdc0_info = { + .clock_source = LCDC_CLK_BUS, + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, + .fourcc = V4L2_PIX_FMT_RGB565, + .interface_type = RGB24, + .clock_divider = 5, + .flags = 0, + .lcd_modes = &lcdc0_mode, + .num_modes = 1, + .panel_cfg = { + .width = 111, + .height = 68, + }, + }, +}; + +static struct resource lcdc0_resources[] = { + [0] = { + .name = "LCD0", + .start = 0xfe940000, + .end = 0xfe943fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(177), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device lcdc0_device = { + .name = "sh_mobile_lcdc_fb", + .num_resources = ARRAY_SIZE(lcdc0_resources), + .resource = lcdc0_resources, + .id = 0, + .dev = { + .platform_data = &lcdc0_info, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +/* + * LCDC1/HDMI + */ +static struct sh_mobile_hdmi_info hdmi_info = { + .flags = HDMI_OUTPUT_PUSH_PULL | + HDMI_OUTPUT_POLARITY_HI | + HDMI_32BIT_REG | + HDMI_HAS_HTOP1 | + HDMI_SND_SRC_SPDIF, +}; + +static struct resource hdmi_resources[] = { + [0] = { + .name = "HDMI", + .start = 0xe6be0000, + .end = 0xe6be03ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(131), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "HDMI emma3pf", + .start = 0xe6be4000, + .end = 0xe6be43ff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device hdmi_device = { + .name = "sh-mobile-hdmi", + .num_resources = ARRAY_SIZE(hdmi_resources), + .resource = hdmi_resources, + .id = -1, + .dev = { + .platform_data = &hdmi_info, + }, +}; + +static const struct fb_videomode lcdc1_mode = { + .name = "HDMI 720p", + .xres = 1280, + .yres = 720, + .pixclock = 13468, + .left_margin = 220, + .right_margin = 110, + .hsync_len = 40, + .upper_margin = 20, + .lower_margin = 5, + .vsync_len = 5, + .refresh = 60, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, +}; + +static struct sh_mobile_lcdc_info hdmi_lcdc_info = { + .clock_source = LCDC_CLK_PERIPHERAL, /* HDMI clock */ + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, + .fourcc = V4L2_PIX_FMT_RGB565, + .interface_type = RGB24, + .clock_divider = 1, + .flags = LCDC_FLAGS_DWPOL, + .lcd_modes = &lcdc1_mode, + .num_modes = 1, + .tx_dev = &hdmi_device, + .panel_cfg = { + .width = 1280, + .height = 720, + }, + }, +}; + +static struct resource hdmi_lcdc_resources[] = { + [0] = { + .name = "LCDC1", + .start = 0xfe944000, + .end = 0xfe948000 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(178), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device hdmi_lcdc_device = { + .name = "sh_mobile_lcdc_fb", + .num_resources = ARRAY_SIZE(hdmi_lcdc_resources), + .resource = hdmi_lcdc_resources, + .id = 1, + .dev = { + .platform_data = &hdmi_lcdc_info, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +/* LEDS */ +static struct gpio_led gpio_leds[] = { + { + .name = "LED3", + .gpio = 102, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, { + .name = "LED4", + .gpio = 111, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, { + .name = "LED5", + .gpio = 110, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, { + .name = "LED6", + .gpio = 177, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, +}; + +static struct gpio_led_platform_data leds_gpio_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device leds_gpio_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &leds_gpio_info, + }, +}; + +/* GPIO KEY */ +#define GPIO_KEY(c, g, d, ...) \ + { .code = c, .gpio = g, .desc = d, .active_low = 1, __VA_ARGS__ } + +static struct gpio_keys_button gpio_buttons[] = { + GPIO_KEY(KEY_POWER, 99, "SW3", .wakeup = 1), + GPIO_KEY(KEY_BACK, 100, "SW4"), + GPIO_KEY(KEY_MENU, 97, "SW5"), + GPIO_KEY(KEY_HOME, 98, "SW6"), +}; + +static struct gpio_keys_platform_data gpio_key_info = { + .buttons = gpio_buttons, + .nbuttons = ARRAY_SIZE(gpio_buttons), +}; + +static struct platform_device gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_key_info, + }, +}; + +/* Fixed 3.3V regulator to be used by SDHI1, MMCIF */ +static struct regulator_consumer_supply fixed3v3_power_consumers[] = { + REGULATOR_SUPPLY("vmmc", "sh_mmcif"), + REGULATOR_SUPPLY("vqmmc", "sh_mmcif"), +}; + +/* Fixed 3.3V regulator used by LCD backlight */ +static struct regulator_consumer_supply fixed5v0_power_consumers[] = { + REGULATOR_SUPPLY("power", "pwm-backlight.0"), +}; + +/* Fixed 3.3V regulator to be used by SDHI0 */ +static struct regulator_consumer_supply vcc_sdhi0_consumers[] = { + REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"), +}; + +static struct regulator_init_data vcc_sdhi0_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(vcc_sdhi0_consumers), + .consumer_supplies = vcc_sdhi0_consumers, +}; + +static struct fixed_voltage_config vcc_sdhi0_info = { + .supply_name = "SDHI0 Vcc", + .microvolts = 3300000, + .gpio = 75, + .enable_high = 1, + .init_data = &vcc_sdhi0_init_data, +}; + +static struct platform_device vcc_sdhi0 = { + .name = "reg-fixed-voltage", + .id = 1, + .dev = { + .platform_data = &vcc_sdhi0_info, + }, +}; + +/* 1.8 / 3.3V SDHI0 VccQ regulator */ +static struct regulator_consumer_supply vccq_sdhi0_consumers[] = { + REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"), +}; + +static struct regulator_init_data vccq_sdhi0_init_data = { + .constraints = { + .input_uV = 3300000, + .min_uV = 1800000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(vccq_sdhi0_consumers), + .consumer_supplies = vccq_sdhi0_consumers, +}; + +static struct gpio vccq_sdhi0_gpios[] = { + {17, GPIOF_OUT_INIT_LOW, "vccq-sdhi0" }, +}; + +static struct gpio_regulator_state vccq_sdhi0_states[] = { + { .value = 3300000, .gpios = (0 << 0) }, + { .value = 1800000, .gpios = (1 << 0) }, +}; + +static struct gpio_regulator_config vccq_sdhi0_info = { + .supply_name = "vqmmc", + + .enable_gpio = 74, + .enable_high = 1, + .enabled_at_boot = 0, + + .gpios = vccq_sdhi0_gpios, + .nr_gpios = ARRAY_SIZE(vccq_sdhi0_gpios), + + .states = vccq_sdhi0_states, + .nr_states = ARRAY_SIZE(vccq_sdhi0_states), + + .type = REGULATOR_VOLTAGE, + .init_data = &vccq_sdhi0_init_data, +}; + +static struct platform_device vccq_sdhi0 = { + .name = "gpio-regulator", + .id = -1, + .dev = { + .platform_data = &vccq_sdhi0_info, + }, +}; + +/* Fixed 3.3V regulator to be used by SDHI1 */ +static struct regulator_consumer_supply vcc_sdhi1_consumers[] = { + REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"), +}; + +static struct regulator_init_data vcc_sdhi1_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(vcc_sdhi1_consumers), + .consumer_supplies = vcc_sdhi1_consumers, +}; + +static struct fixed_voltage_config vcc_sdhi1_info = { + .supply_name = "SDHI1 Vcc", + .microvolts = 3300000, + .gpio = 16, + .enable_high = 1, + .init_data = &vcc_sdhi1_init_data, +}; + +static struct platform_device vcc_sdhi1 = { + .name = "reg-fixed-voltage", + .id = 2, + .dev = { + .platform_data = &vcc_sdhi1_info, + }, +}; + +/* SDHI0 */ +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_POWER_OFF_CARD, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, + .cd_gpio = 167, +}; + +static struct resource sdhi0_resources[] = { + { + .name = "SDHI0", + .start = 0xe6850000, + .end = 0xe6850100 - 1, + .flags = IORESOURCE_MEM, + }, + /* + * no SH_MOBILE_SDHI_IRQ_CARD_DETECT here + */ + { + .name = SH_MOBILE_SDHI_IRQ_SDCARD, + .start = gic_spi(118), + .flags = IORESOURCE_IRQ, + }, + { + .name = SH_MOBILE_SDHI_IRQ_SDIO, + .start = gic_spi(119), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sdhi0_device = { + .name = "sh_mobile_sdhi", + .id = 0, + .dev = { + .platform_data = &sdhi0_info, + }, + .num_resources = ARRAY_SIZE(sdhi0_resources), + .resource = sdhi0_resources, +}; + +/* SDHI1 */ +static struct tmio_mmc_data sdhi1_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_POWER_OFF_CARD, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, + /* Port72 cannot generate IRQs, will be used in polling mode. */ + .cd_gpio = 72, +}; + +static struct resource sdhi1_resources[] = { + [0] = { + .name = "SDHI1", + .start = 0xe6860000, + .end = 0xe6860100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(121), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = gic_spi(122), + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = gic_spi(123), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sdhi1_device = { + .name = "sh_mobile_sdhi", + .id = 1, + .dev = { + .platform_data = &sdhi1_info, + }, + .num_resources = ARRAY_SIZE(sdhi1_resources), + .resource = sdhi1_resources, +}; + +static const struct pinctrl_map eva_sdhi1_pinctrl_map[] = { + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7740", + "sdhi1_data4", "sdhi1"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7740", + "sdhi1_ctrl", "sdhi1"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7740", + "sdhi1_cd", "sdhi1"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7740", + "sdhi1_wp", "sdhi1"), +}; + +/* MMCIF */ +static struct sh_mmcif_plat_data sh_mmcif_plat = { + .sup_pclk = 0, + .caps = MMC_CAP_4_BIT_DATA | + MMC_CAP_8_BIT_DATA | + MMC_CAP_NONREMOVABLE, + .ccs_unsupported = true, + .slave_id_tx = SHDMA_SLAVE_MMCIF_TX, + .slave_id_rx = SHDMA_SLAVE_MMCIF_RX, +}; + +static struct resource sh_mmcif_resources[] = { + [0] = { + .name = "MMCIF", + .start = 0xe6bd0000, + .end = 0xe6bd0100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* MMC ERR */ + .start = gic_spi(56), + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* MMC NOR */ + .start = gic_spi(57), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sh_mmcif_device = { + .name = "sh_mmcif", + .id = -1, + .dev = { + .platform_data = &sh_mmcif_plat, + }, + .num_resources = ARRAY_SIZE(sh_mmcif_resources), + .resource = sh_mmcif_resources, +}; + +/* Camera */ +static int mt9t111_power(struct device *dev, int mode) +{ + struct clk *mclk = clk_get(NULL, "video1"); + + if (IS_ERR(mclk)) { + dev_err(dev, "can't get video1 clock\n"); + return -EINVAL; + } + + if (mode) { + /* video1 (= CON1 camera) expect 24MHz */ + clk_set_rate(mclk, clk_round_rate(mclk, 24000000)); + clk_enable(mclk); + gpio_set_value(158, 1); + } else { + gpio_set_value(158, 0); + clk_disable(mclk); + } + + clk_put(mclk); + + return 0; +} + +static struct i2c_board_info i2c_camera_mt9t111 = { + I2C_BOARD_INFO("mt9t112", 0x3d), +}; + +static struct mt9t112_camera_info mt9t111_info = { + .divider = { 16, 0, 0, 7, 0, 10, 14, 7, 7 }, +}; + +static struct soc_camera_link mt9t111_link = { + .i2c_adapter_id = 0, + .bus_id = 0, + .board_info = &i2c_camera_mt9t111, + .power = mt9t111_power, + .priv = &mt9t111_info, +}; + +static struct platform_device camera_device = { + .name = "soc-camera-pdrv", + .id = 0, + .dev = { + .platform_data = &mt9t111_link, + }, +}; + +/* CEU0 */ +static struct sh_mobile_ceu_info sh_mobile_ceu0_info = { + .flags = SH_CEU_FLAG_LOWER_8BIT, +}; + +static struct resource ceu0_resources[] = { + [0] = { + .name = "CEU", + .start = 0xfe910000, + .end = 0xfe91009f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(160), + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device ceu0_device = { + .name = "sh_mobile_ceu", + .id = 0, + .num_resources = ARRAY_SIZE(ceu0_resources), + .resource = ceu0_resources, + .dev = { + .platform_data = &sh_mobile_ceu0_info, + .coherent_dma_mask = 0xffffffff, + }, +}; + +/* FSI */ +static struct sh_fsi_platform_info fsi_info = { + /* FSI-WM8978 */ + .port_a = { + .tx_id = SHDMA_SLAVE_FSIA_TX, + }, + /* FSI-HDMI */ + .port_b = { + .flags = SH_FSI_FMT_SPDIF | + SH_FSI_ENABLE_STREAM_MODE | + SH_FSI_CLK_CPG, + .tx_id = SHDMA_SLAVE_FSIB_TX, + } +}; + +static struct resource fsi_resources[] = { + [0] = { + .name = "FSI", + .start = 0xfe1f0000, + .end = 0xfe1f0400 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(9), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device fsi_device = { + .name = "sh_fsi2", + .id = -1, + .num_resources = ARRAY_SIZE(fsi_resources), + .resource = fsi_resources, + .dev = { + .platform_data = &fsi_info, + }, +}; + +/* FSI-WM8978 */ +static struct asoc_simple_card_info fsi_wm8978_info = { + .name = "wm8978", + .card = "FSI2A-WM8978", + .codec = "wm8978.0-001a", + .platform = "sh_fsi2", + .daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .cpu_dai = { + .name = "fsia-dai", + }, + .codec_dai = { + .name = "wm8978-hifi", + .sysclk = 12288000, + }, +}; + +static struct platform_device fsi_wm8978_device = { + .name = "asoc-simple-card", + .id = 0, + .dev = { + .platform_data = &fsi_wm8978_info, + .coherent_dma_mask = DMA_BIT_MASK(32), + .dma_mask = &fsi_wm8978_device.dev.coherent_dma_mask, + }, +}; + +/* FSI-HDMI */ +static struct asoc_simple_card_info fsi2_hdmi_info = { + .name = "HDMI", + .card = "FSI2B-HDMI", + .codec = "sh-mobile-hdmi", + .platform = "sh_fsi2", + .daifmt = SND_SOC_DAIFMT_CBS_CFS, + .cpu_dai = { + .name = "fsib-dai", + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, +}; + +static struct platform_device fsi_hdmi_device = { + .name = "asoc-simple-card", + .id = 1, + .dev = { + .platform_data = &fsi2_hdmi_info, + .coherent_dma_mask = DMA_BIT_MASK(32), + .dma_mask = &fsi_hdmi_device.dev.coherent_dma_mask, + }, +}; + +/* RTC: RTC connects i2c-gpio. */ +static struct i2c_gpio_platform_data i2c_gpio_data = { + .sda_pin = 208, + .scl_pin = 91, + .udelay = 5, /* 100 kHz */ +}; + +static struct platform_device i2c_gpio_device = { + .name = "i2c-gpio", + .id = 2, + .dev = { + .platform_data = &i2c_gpio_data, + }, +}; + +/* I2C */ +static struct st1232_pdata st1232_i2c0_pdata = { + .reset_gpio = 166, +}; + +static struct i2c_board_info i2c0_devices[] = { + { + I2C_BOARD_INFO("st1232-ts", 0x55), + .irq = irq_pin(10), + .platform_data = &st1232_i2c0_pdata, + }, + { + I2C_BOARD_INFO("wm8978", 0x1a), + }, +}; + +static struct i2c_board_info i2c2_devices[] = { + { + I2C_BOARD_INFO("s35390a", 0x30), + .type = "s35390a", + }, +}; + +/* + * board devices + */ +static struct platform_device *eva_devices[] __initdata = { + &lcdc0_device, + &pwm_device, + &pwm_backlight_device, + &leds_gpio_device, + &gpio_keys_device, + &sh_eth_device, + &vcc_sdhi0, + &vccq_sdhi0, + &sdhi0_device, + &sh_mmcif_device, + &hdmi_device, + &hdmi_lcdc_device, + &camera_device, + &ceu0_device, + &fsi_device, + &fsi_wm8978_device, + &fsi_hdmi_device, + &i2c_gpio_device, +}; + +static const struct pinctrl_map eva_pinctrl_map[] = { + /* CEU0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_ceu.0", "pfc-r8a7740", + "ceu0_data_0_7", "ceu0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_ceu.0", "pfc-r8a7740", + "ceu0_clk_0", "ceu0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_ceu.0", "pfc-r8a7740", + "ceu0_sync", "ceu0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_ceu.0", "pfc-r8a7740", + "ceu0_field", "ceu0"), + /* FSIA */ + PIN_MAP_MUX_GROUP_DEFAULT("asoc-simple-card.0", "pfc-r8a7740", + "fsia_sclk_in", "fsia"), + PIN_MAP_MUX_GROUP_DEFAULT("asoc-simple-card.0", "pfc-r8a7740", + "fsia_mclk_out", "fsia"), + PIN_MAP_MUX_GROUP_DEFAULT("asoc-simple-card.0", "pfc-r8a7740", + "fsia_data_in_1", "fsia"), + PIN_MAP_MUX_GROUP_DEFAULT("asoc-simple-card.0", "pfc-r8a7740", + "fsia_data_out_0", "fsia"), + /* FSIB */ + PIN_MAP_MUX_GROUP_DEFAULT("asoc-simple-card.1", "pfc-r8a7740", + "fsib_mclk_in", "fsib"), + /* GETHER */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7740-gether", "pfc-r8a7740", + "gether_mii", "gether"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7740-gether", "pfc-r8a7740", + "gether_int", "gether"), + /* HDMI */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-mobile-hdmi", "pfc-r8a7740", + "hdmi", "hdmi"), + /* LCD0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "pfc-r8a7740", + "lcd0_data24_0", "lcd0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "pfc-r8a7740", + "lcd0_lclk_1", "lcd0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "pfc-r8a7740", + "lcd0_sync", "lcd0"), + /* MMCIF */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-r8a7740", + "mmc0_data8_1", "mmc0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-r8a7740", + "mmc0_ctrl_1", "mmc0"), + /* SCIFA1 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.1", "pfc-r8a7740", + "scifa1_data", "scifa1"), + /* SDHI0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7740", + "sdhi0_data4", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7740", + "sdhi0_ctrl", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7740", + "sdhi0_wp", "sdhi0"), + /* ST1232 */ + PIN_MAP_MUX_GROUP_DEFAULT("0-0055", "pfc-r8a7740", + "intc_irq10", "intc"), + /* TPU0 */ + PIN_MAP_MUX_GROUP_DEFAULT("renesas-tpu-pwm", "pfc-r8a7740", + "tpu0_to2_1", "tpu0"), + /* USBHS */ + PIN_MAP_MUX_GROUP_DEFAULT("renesas_usbhs", "pfc-r8a7740", + "intc_irq7_1", "intc"), +}; + +static void __init eva_clock_init(void) +{ + struct clk *system = clk_get(NULL, "system_clk"); + struct clk *xtal1 = clk_get(NULL, "extal1"); + struct clk *usb24s = clk_get(NULL, "usb24s"); + struct clk *fsibck = clk_get(NULL, "fsibck"); + + if (IS_ERR(system) || + IS_ERR(xtal1) || + IS_ERR(usb24s) || + IS_ERR(fsibck)) { + pr_err("armadillo800eva board clock init failed\n"); + goto clock_error; + } + + /* armadillo 800 eva extal1 is 24MHz */ + clk_set_rate(xtal1, 24000000); + + /* usb24s use extal1 (= system) clock (= 24MHz) */ + clk_set_parent(usb24s, system); + + /* FSIBCK is 12.288MHz, and it is parent of FSI-B */ + clk_set_rate(fsibck, 12288000); + +clock_error: + if (!IS_ERR(system)) + clk_put(system); + if (!IS_ERR(xtal1)) + clk_put(xtal1); + if (!IS_ERR(usb24s)) + clk_put(usb24s); + if (!IS_ERR(fsibck)) + clk_put(fsibck); +} + +/* + * board init + */ +#define GPIO_PORT7CR IOMEM(0xe6050007) +#define GPIO_PORT8CR IOMEM(0xe6050008) +static void __init eva_init(void) +{ + static struct pm_domain_device domain_devices[] __initdata = { + { "A4LC", &lcdc0_device }, + { "A4LC", &hdmi_lcdc_device }, + { "A4MP", &hdmi_device }, + { "A4MP", &fsi_device }, + { "A4R", &ceu0_device }, + { "A4S", &sh_eth_device }, + { "A3SP", &pwm_device }, + { "A3SP", &sdhi0_device }, + { "A3SP", &sh_mmcif_device }, + }; + struct platform_device *usb = NULL, *sdhi1 = NULL; + + regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers, + ARRAY_SIZE(fixed3v3_power_consumers), 3300000); + regulator_register_always_on(3, "fixed-5.0V", fixed5v0_power_consumers, + ARRAY_SIZE(fixed5v0_power_consumers), 5000000); + + pinctrl_register_mappings(eva_pinctrl_map, ARRAY_SIZE(eva_pinctrl_map)); + pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup)); + + r8a7740_pinmux_init(); + r8a7740_meram_workaround(); + + /* GETHER */ + gpio_request_one(18, GPIOF_OUT_INIT_HIGH, NULL); /* PHY_RST */ + + /* USB */ + gpio_request_one(159, GPIOF_IN, NULL); /* USB_DEVICE_MODE */ + + if (gpio_get_value(159)) { + /* USB Host */ + } else { + /* USB Func */ + /* + * The USBHS interrupt handlers needs to read the IRQ pin value + * (HI/LOW) to diffentiate USB connection and disconnection + * events (usbhsf_get_vbus()). We thus need to select both the + * intc_irq7_1 pin group and GPIO 209 here. + */ + gpio_request_one(209, GPIOF_IN, NULL); + + platform_device_register(&usbhsf_device); + usb = &usbhsf_device; + } + + /* CON1/CON15 Camera */ + gpio_request_one(173, GPIOF_OUT_INIT_LOW, NULL); /* STANDBY */ + gpio_request_one(172, GPIOF_OUT_INIT_HIGH, NULL); /* RST */ + /* see mt9t111_power() */ + gpio_request_one(158, GPIOF_OUT_INIT_LOW, NULL); /* CAM_PON */ + + /* FSI-WM8978 */ + gpio_request(7, NULL); + gpio_request(8, NULL); + gpio_direction_none(GPIO_PORT7CR); /* FSIAOBT needs no direction */ + gpio_direction_none(GPIO_PORT8CR); /* FSIAOLR needs no direction */ + + /* + * CAUTION + * + * DBGMD/LCDC0/FSIA MUX + * DBGMD_SELECT_B should be set after setting PFC Function. + */ + gpio_request_one(176, GPIOF_OUT_INIT_HIGH, NULL); + + /* + * We can switch CON8/CON14 by SW1.5, + * but it needs after DBGMD_SELECT_B + */ + gpio_request_one(6, GPIOF_IN, NULL); + if (gpio_get_value(6)) { + /* CON14 enable */ + } else { + /* CON8 (SDHI1) enable */ + pinctrl_register_mappings(eva_sdhi1_pinctrl_map, + ARRAY_SIZE(eva_sdhi1_pinctrl_map)); + + platform_device_register(&vcc_sdhi1); + platform_device_register(&sdhi1_device); + sdhi1 = &sdhi1_device; + } + + +#ifdef CONFIG_CACHE_L2X0 + /* Shared attribute override enable, 32K*8way */ + l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff); +#endif + + i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); + i2c_register_board_info(2, i2c2_devices, ARRAY_SIZE(i2c2_devices)); + + r8a7740_add_standard_devices(); + + platform_add_devices(eva_devices, + ARRAY_SIZE(eva_devices)); + + rmobile_add_devices_to_domains(domain_devices, + ARRAY_SIZE(domain_devices)); + if (usb) + rmobile_add_device_to_domain("A3SP", usb); + if (sdhi1) + rmobile_add_device_to_domain("A3SP", sdhi1); + + r8a7740_pm_init(); +} + +static void __init eva_earlytimer_init(void) +{ + r8a7740_clock_init(MD_CK0 | MD_CK2); + shmobile_earlytimer_init(); + + /* the rate of extal1 clock must be set before late_time_init */ + eva_clock_init(); +} + +#define RESCNT2 IOMEM(0xe6188020) +static void eva_restart(enum reboot_mode mode, const char *cmd) +{ + /* Do soft power on reset */ + writel((1 << 31), RESCNT2); +} + +static const char *eva_boards_compat_dt[] __initdata = { + "renesas,armadillo800eva", + NULL, +}; + +DT_MACHINE_START(ARMADILLO800EVA_DT, "armadillo800eva") + .map_io = r8a7740_map_io, + .init_early = r8a7740_add_early_devices, + .init_irq = r8a7740_init_irq_of, + .init_machine = eva_init, + .init_late = shmobile_init_late, + .init_time = eva_earlytimer_init, + .dt_compat = eva_boards_compat_dt, + .restart = eva_restart, +MACHINE_END diff --git a/arch/arm/mach-shmobile/board-bockw-reference.c b/arch/arm/mach-shmobile/board-bockw-reference.c new file mode 100644 index 000000000..9a74efda3 --- /dev/null +++ b/arch/arm/mach-shmobile/board-bockw-reference.c @@ -0,0 +1,86 @@ +/* + * Bock-W board support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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. + */ + +#include <linux/of_platform.h> + +#include <asm/mach/arch.h> + +#include "common.h" +#include "r8a7778.h" + +/* + * see board-bock.c for checking detail of dip-switch + */ + +#define FPGA 0x18200000 +#define IRQ0MR 0x30 +#define COMCTLR 0x101c + +#define PFC 0xfffc0000 +#define PUPR4 0x110 +static void __init bockw_init(void) +{ + void __iomem *fpga; + void __iomem *pfc; + +#ifndef CONFIG_COMMON_CLK + r8a7778_clock_init(); +#endif + r8a7778_init_irq_extpin_dt(1); + r8a7778_add_dt_devices(); + + fpga = ioremap_nocache(FPGA, SZ_1M); + if (fpga) { + /* + * CAUTION + * + * IRQ0/1 is cascaded interrupt from FPGA. + * it should be cared in the future + * Now, it is assuming IRQ0 was used only from SMSC. + */ + u16 val = ioread16(fpga + IRQ0MR); + val &= ~(1 << 4); /* enable SMSC911x */ + iowrite16(val, fpga + IRQ0MR); + + iounmap(fpga); + } + + pfc = ioremap_nocache(PFC, 0x200); + if (pfc) { + /* + * FIXME + * + * SDHI CD/WP pin needs pull-up + */ + iowrite32(ioread32(pfc + PUPR4) | (3 << 26), pfc + PUPR4); + iounmap(pfc); + } + + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *bockw_boards_compat_dt[] __initdata = { + "renesas,bockw-reference", + NULL, +}; + +DT_MACHINE_START(BOCKW_DT, "bockw") + .init_early = shmobile_init_delay, + .init_irq = r8a7778_init_irq_dt, + .init_machine = bockw_init, + .init_late = shmobile_init_late, + .dt_compat = bockw_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c new file mode 100644 index 000000000..25558d1f4 --- /dev/null +++ b/arch/arm/mach-shmobile/board-bockw.c @@ -0,0 +1,737 @@ +/* + * Bock-W board support + * + * Copyright (C) 2013-2014 Renesas Solutions Corp. + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Copyright (C) 2013-2014 Cogent Embedded, Inc. + * + * 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. + */ + +#include <linux/mfd/tmio.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sh_mobile_sdhi.h> +#include <linux/mmc/sh_mmcif.h> +#include <linux/mtd/partitions.h> +#include <linux/pinctrl/machine.h> +#include <linux/platform_data/camera-rcar.h> +#include <linux/platform_data/usb-rcar-phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/machine.h> +#include <linux/smsc911x.h> +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> +#include <linux/usb/renesas_usbhs.h> + +#include <media/soc_camera.h> +#include <asm/mach/arch.h> +#include <sound/rcar_snd.h> +#include <sound/simple_card.h> + +#include "common.h" +#include "irqs.h" +#include "r8a7778.h" + +#define FPGA 0x18200000 +#define IRQ0MR 0x30 +#define COMCTLR 0x101c +static void __iomem *fpga; + +/* + * CN9(Upper side) SCIF/RCAN selection + * + * 1,4 3,6 + * SW40 SCIF RCAN + * SW41 SCIF RCAN + */ + +/* + * MMC (CN26) pin + * + * SW6 (D2) 3 pin + * SW7 (D5) ON + * SW8 (D3) 3 pin + * SW10 (D4) 1 pin + * SW12 (CLK) 1 pin + * SW13 (D6) 3 pin + * SW14 (CMD) ON + * SW15 (D6) 1 pin + * SW16 (D0) ON + * SW17 (D1) ON + * SW18 (D7) 3 pin + * SW19 (MMC) 1 pin + */ + +/* + * SSI settings + * + * SW45: 1-4 side (SSI5 out, ROUT/LOUT CN19 Mid) + * SW46: 1101 (SSI6 Recorde) + * SW47: 1110 (SSI5 Playback) + * SW48: 11 (Recorde power) + * SW49: 1 (SSI slave mode) + * SW50: 1111 (SSI7, SSI8) + * SW51: 1111 (SSI3, SSI4) + * SW54: 1pin (ak4554 FPGA control) + * SW55: 1 (CLKB is 24.5760MHz) + * SW60: 1pin (ak4554 FPGA control) + * SW61: 3pin (use X11 clock) + * SW78: 3-6 (ak4642 connects I2C0) + * + * You can use sound as + * + * hw0: CN19: SSI56-AK4643 + * hw1: CN21: SSI3-AK4554(playback) + * hw2: CN21: SSI4-AK4554(capture) + * hw3: CN20: SSI7-AK4554(playback) + * hw4: CN20: SSI8-AK4554(capture) + * + * this command is required when playback on hw0. + * + * # amixer set "LINEOUT Mixer DACL" on + */ + +/* + * USB + * + * USB1 (CN29) can be Host/Function + * + * Host Func + * SW98 1 2 + * SW99 1 3 + */ + +/* Dummy supplies, where voltage doesn't matter */ +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x"), + REGULATOR_SUPPLY("vdd33a", "smsc911x"), +}; + +static struct regulator_consumer_supply fixed3v3_power_consumers[] = { + REGULATOR_SUPPLY("vmmc", "sh_mmcif"), + REGULATOR_SUPPLY("vqmmc", "sh_mmcif"), +}; + +static struct smsc911x_platform_config smsc911x_data __initdata = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_32BIT, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct resource smsc911x_resources[] __initdata = { + DEFINE_RES_MEM(0x18300000, 0x1000), + DEFINE_RES_IRQ(irq_pin(0)), /* IRQ 0 */ +}; + +#if IS_ENABLED(CONFIG_USB_RENESAS_USBHS_UDC) +/* + * When USB1 is Func + */ +static int usbhsf_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +#define SUSPMODE 0x102 +static int usbhsf_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + enable = !!enable; + + r8a7778_usb_phy_power(enable); + + iowrite16(enable << 14, base + SUSPMODE); + + return 0; +} + +static struct resource usbhsf_resources[] __initdata = { + DEFINE_RES_MEM(0xffe60000, 0x110), + DEFINE_RES_IRQ(gic_iid(0x4f)), +}; + +static struct renesas_usbhs_platform_info usbhs_info __initdata = { + .platform_callback = { + .get_id = usbhsf_get_id, + .power_ctrl = usbhsf_power_ctrl, + }, + .driver_param = { + .buswait_bwait = 4, + .d0_tx_id = HPBDMA_SLAVE_USBFUNC_TX, + .d1_rx_id = HPBDMA_SLAVE_USBFUNC_RX, + }, +}; + +#define USB_PHY_SETTING {.port1_func = 1, .ovc_pin[1].active_high = 1,} +#define USB1_DEVICE "renesas_usbhs" +#define ADD_USB_FUNC_DEVICE_IF_POSSIBLE() \ + platform_device_register_resndata( \ + NULL, "renesas_usbhs", -1, \ + usbhsf_resources, \ + ARRAY_SIZE(usbhsf_resources), \ + &usbhs_info, sizeof(struct renesas_usbhs_platform_info)) + +#else +/* + * When USB1 is Host + */ +#define USB_PHY_SETTING { } +#define USB1_DEVICE "ehci-platform" +#define ADD_USB_FUNC_DEVICE_IF_POSSIBLE() + +#endif + +/* USB */ +static struct resource usb_phy_resources[] __initdata = { + DEFINE_RES_MEM(0xffe70800, 0x100), + DEFINE_RES_MEM(0xffe76000, 0x100), +}; + +static struct rcar_phy_platform_data usb_phy_platform_data __initdata = + USB_PHY_SETTING; + + +/* SDHI */ +static struct tmio_mmc_data sdhi0_info __initdata = { + .chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED, + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34, + .flags = TMIO_MMC_HAS_IDLE_WAIT, +}; + +static struct resource sdhi0_resources[] __initdata = { + DEFINE_RES_MEM(0xFFE4C000, 0x100), + DEFINE_RES_IRQ(gic_iid(0x77)), +}; + +/* Ether */ +static struct resource ether_resources[] __initdata = { + DEFINE_RES_MEM(0xfde00000, 0x400), + DEFINE_RES_IRQ(gic_iid(0x89)), +}; + +static struct sh_eth_plat_data ether_platform_data __initdata = { + .phy = 0x01, + .edmac_endian = EDMAC_LITTLE_ENDIAN, + .phy_interface = PHY_INTERFACE_MODE_RMII, + /* + * Although the LINK signal is available on the board, it's connected to + * the link/activity LED output of the PHY, thus the link disappears and + * reappears after each packet. We'd be better off ignoring such signal + * and getting the link state from the PHY indirectly. + */ + .no_ether_link = 1, +}; + +static struct platform_device_info ether_info __initdata = { + .name = "r8a777x-ether", + .id = -1, + .res = ether_resources, + .num_res = ARRAY_SIZE(ether_resources), + .data = ðer_platform_data, + .size_data = sizeof(ether_platform_data), + .dma_mask = DMA_BIT_MASK(32), +}; + +/* I2C */ +static struct i2c_board_info i2c0_devices[] = { + { + I2C_BOARD_INFO("rx8581", 0x51), + }, { + I2C_BOARD_INFO("ak4643", 0x12), + } +}; + +/* HSPI*/ +static struct mtd_partition m25p80_spi_flash_partitions[] = { + { + .name = "data(spi)", + .size = 0x0100000, + .offset = 0, + }, +}; + +static struct flash_platform_data spi_flash_data = { + .name = "m25p80", + .type = "s25fl008k", + .parts = m25p80_spi_flash_partitions, + .nr_parts = ARRAY_SIZE(m25p80_spi_flash_partitions), +}; + +static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "m25p80", + .max_speed_hz = 104000000, + .chip_select = 0, + .bus_num = 0, + .mode = SPI_MODE_0, + .platform_data = &spi_flash_data, + }, +}; + +/* MMC */ +static struct resource mmc_resources[] __initdata = { + DEFINE_RES_MEM(0xffe4e000, 0x100), + DEFINE_RES_IRQ(gic_iid(0x5d)), +}; + +static struct sh_mmcif_plat_data sh_mmcif_plat __initdata = { + .sup_pclk = 0, + .caps = MMC_CAP_4_BIT_DATA | + MMC_CAP_8_BIT_DATA | + MMC_CAP_NEEDS_POLL, +}; + +/* In the default configuration both decoders reside on I2C bus 0 */ +#define BOCKW_CAMERA(idx) \ +static struct i2c_board_info camera##idx##_info = { \ + I2C_BOARD_INFO("ml86v7667", 0x41 + 2 * (idx)), \ +}; \ + \ +static struct soc_camera_link iclink##idx##_ml86v7667 __initdata = { \ + .bus_id = idx, \ + .i2c_adapter_id = 0, \ + .board_info = &camera##idx##_info, \ +} + +BOCKW_CAMERA(0); +BOCKW_CAMERA(1); + +/* VIN */ +static struct rcar_vin_platform_data vin_platform_data __initdata = { + .flags = RCAR_VIN_BT656, +}; + +#define R8A7778_VIN(idx) \ +static struct resource vin##idx##_resources[] __initdata = { \ + DEFINE_RES_MEM(0xffc50000 + 0x1000 * (idx), 0x1000), \ + DEFINE_RES_IRQ(gic_iid(0x5a)), \ +}; \ + \ +static struct platform_device_info vin##idx##_info __initdata = { \ + .name = "r8a7778-vin", \ + .id = idx, \ + .res = vin##idx##_resources, \ + .num_res = ARRAY_SIZE(vin##idx##_resources), \ + .dma_mask = DMA_BIT_MASK(32), \ + .data = &vin_platform_data, \ + .size_data = sizeof(vin_platform_data), \ +} +R8A7778_VIN(0); +R8A7778_VIN(1); + +/* Sound */ +static struct resource rsnd_resources[] __initdata = { + [RSND_GEN1_SRU] = DEFINE_RES_MEM(0xffd90000, 0x1000), + [RSND_GEN1_SSI] = DEFINE_RES_MEM(0xffd91000, 0x1240), + [RSND_GEN1_ADG] = DEFINE_RES_MEM(0xfffe0000, 0x24), +}; + +static struct rsnd_ssi_platform_info rsnd_ssi[] = { + RSND_SSI_UNUSED, /* SSI 0 */ + RSND_SSI_UNUSED, /* SSI 1 */ + RSND_SSI_UNUSED, /* SSI 2 */ + RSND_SSI(HPBDMA_SLAVE_HPBIF3_TX, gic_iid(0x85), 0), + RSND_SSI(HPBDMA_SLAVE_HPBIF4_RX, gic_iid(0x85), RSND_SSI_CLK_PIN_SHARE), + RSND_SSI(HPBDMA_SLAVE_HPBIF5_TX, gic_iid(0x86), 0), + RSND_SSI(HPBDMA_SLAVE_HPBIF6_RX, gic_iid(0x86), 0), + RSND_SSI(HPBDMA_SLAVE_HPBIF7_TX, gic_iid(0x86), 0), + RSND_SSI(HPBDMA_SLAVE_HPBIF8_RX, gic_iid(0x86), RSND_SSI_CLK_PIN_SHARE), +}; + +static struct rsnd_src_platform_info rsnd_src[9] = { + RSND_SRC_UNUSED, /* SRU 0 */ + RSND_SRC_UNUSED, /* SRU 1 */ + RSND_SRC_UNUSED, /* SRU 2 */ + RSND_SRC(0, 0), + RSND_SRC(0, 0), + RSND_SRC(0, 0), + RSND_SRC(0, 0), + RSND_SRC(0, 0), + RSND_SRC(0, 0), +}; + +static struct rsnd_dai_platform_info rsnd_dai[] = { + { + .playback = { .ssi = &rsnd_ssi[5], .src = &rsnd_src[5] }, + .capture = { .ssi = &rsnd_ssi[6], .src = &rsnd_src[6] }, + }, { + .playback = { .ssi = &rsnd_ssi[3], .src = &rsnd_src[3] }, + }, { + .capture = { .ssi = &rsnd_ssi[4], .src = &rsnd_src[4] }, + }, { + .playback = { .ssi = &rsnd_ssi[7], .src = &rsnd_src[7] }, + }, { + .capture = { .ssi = &rsnd_ssi[8], .src = &rsnd_src[8] }, + }, +}; + +enum { + AK4554_34 = 0, + AK4643_56, + AK4554_78, + SOUND_MAX, +}; + +static int rsnd_codec_power(int id, int enable) +{ + static int sound_user[SOUND_MAX] = {0, 0, 0}; + int *usr = NULL; + u32 bit; + + switch (id) { + case 3: + case 4: + usr = sound_user + AK4554_34; + bit = (1 << 10); + break; + case 5: + case 6: + usr = sound_user + AK4643_56; + bit = (1 << 6); + break; + case 7: + case 8: + usr = sound_user + AK4554_78; + bit = (1 << 7); + break; + } + + if (!usr) + return -EIO; + + if (enable) { + if (*usr == 0) { + u32 val = ioread16(fpga + COMCTLR); + val &= ~bit; + iowrite16(val, fpga + COMCTLR); + } + + (*usr)++; + } else { + if (*usr == 0) + return 0; + + (*usr)--; + + if (*usr == 0) { + u32 val = ioread16(fpga + COMCTLR); + val |= bit; + iowrite16(val, fpga + COMCTLR); + } + } + + return 0; +} + +static int rsnd_start(int id) +{ + return rsnd_codec_power(id, 1); +} + +static int rsnd_stop(int id) +{ + return rsnd_codec_power(id, 0); +} + +static struct rcar_snd_info rsnd_info = { + .flags = RSND_GEN1, + .ssi_info = rsnd_ssi, + .ssi_info_nr = ARRAY_SIZE(rsnd_ssi), + .src_info = rsnd_src, + .src_info_nr = ARRAY_SIZE(rsnd_src), + .dai_info = rsnd_dai, + .dai_info_nr = ARRAY_SIZE(rsnd_dai), + .start = rsnd_start, + .stop = rsnd_stop, +}; + +static struct asoc_simple_card_info rsnd_card_info[] = { + /* SSI5, SSI6 */ + { + .name = "AK4643", + .card = "SSI56-AK4643", + .codec = "ak4642-codec.0-0012", + .platform = "rcar_sound", + .daifmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM, + .cpu_dai = { + .name = "rsnd-dai.0", + }, + .codec_dai = { + .name = "ak4642-hifi", + .sysclk = 11289600, + }, + }, + /* SSI3 */ + { + .name = "AK4554", + .card = "SSI3-AK4554(playback)", + .codec = "ak4554-adc-dac.0", + .platform = "rcar_sound", + .daifmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_RIGHT_J, + .cpu_dai = { + .name = "rsnd-dai.1", + }, + .codec_dai = { + .name = "ak4554-hifi", + }, + }, + /* SSI4 */ + { + .name = "AK4554", + .card = "SSI4-AK4554(capture)", + .codec = "ak4554-adc-dac.0", + .platform = "rcar_sound", + .daifmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "rsnd-dai.2", + }, + .codec_dai = { + .name = "ak4554-hifi", + }, + }, + /* SSI7 */ + { + .name = "AK4554", + .card = "SSI7-AK4554(playback)", + .codec = "ak4554-adc-dac.1", + .platform = "rcar_sound", + .daifmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_RIGHT_J, + .cpu_dai = { + .name = "rsnd-dai.3", + }, + .codec_dai = { + .name = "ak4554-hifi", + }, + }, + /* SSI8 */ + { + .name = "AK4554", + .card = "SSI8-AK4554(capture)", + .codec = "ak4554-adc-dac.1", + .platform = "rcar_sound", + .daifmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "rsnd-dai.4", + }, + .codec_dai = { + .name = "ak4554-hifi", + }, + } +}; + +static const struct pinctrl_map bockw_pinctrl_map[] = { + /* AUDIO */ + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "audio_clk_a", "audio_clk"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "audio_clk_b", "audio_clk"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi34_ctrl", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi3_data", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi4_data", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi5_ctrl", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi5_data", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi6_ctrl", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi6_data", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi78_ctrl", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi7_data", "ssi"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7778", + "ssi8_data", "ssi"), + /* Ether */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a777x-ether", "pfc-r8a7778", + "ether_rmii", "ether"), + /* HSPI0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-hspi.0", "pfc-r8a7778", + "hspi0_a", "hspi0"), + /* MMC */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif", "pfc-r8a7778", + "mmc_data8", "mmc"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif", "pfc-r8a7778", + "mmc_ctrl", "mmc"), + /* SCIF0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.0", "pfc-r8a7778", + "scif0_data_a", "scif0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.0", "pfc-r8a7778", + "scif0_ctrl", "scif0"), + /* USB */ + PIN_MAP_MUX_GROUP_DEFAULT("ehci-platform", "pfc-r8a7778", + "usb0", "usb0"), + PIN_MAP_MUX_GROUP_DEFAULT(USB1_DEVICE, "pfc-r8a7778", + "usb1", "usb1"), + /* SDHI0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7778", + "sdhi0_data4", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7778", + "sdhi0_ctrl", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7778", + "sdhi0_cd", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7778", + "sdhi0_wp", "sdhi0"), + /* VIN0 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.0", "pfc-r8a7778", + "vin0_clk", "vin0"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.0", "pfc-r8a7778", + "vin0_data8", "vin0"), + /* VIN1 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.1", "pfc-r8a7778", + "vin1_clk", "vin1"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.1", "pfc-r8a7778", + "vin1_data8", "vin1"), +}; + +#define PFC 0xfffc0000 +#define PUPR4 0x110 +static void __init bockw_init(void) +{ + void __iomem *base; + struct clk *clk; + struct platform_device *pdev; + int i; + + r8a7778_clock_init(); + r8a7778_init_irq_extpin(1); + r8a7778_add_standard_devices(); + + platform_device_register_full(ðer_info); + + platform_device_register_full(&vin0_info); + /* VIN1 has a pin conflict with Ether */ + if (!IS_ENABLED(CONFIG_SH_ETH)) + platform_device_register_full(&vin1_info); + platform_device_register_data(NULL, "soc-camera-pdrv", 0, + &iclink0_ml86v7667, + sizeof(iclink0_ml86v7667)); + platform_device_register_data(NULL, "soc-camera-pdrv", 1, + &iclink1_ml86v7667, + sizeof(iclink1_ml86v7667)); + + i2c_register_board_info(0, i2c0_devices, + ARRAY_SIZE(i2c0_devices)); + spi_register_board_info(spi_board_info, + ARRAY_SIZE(spi_board_info)); + pinctrl_register_mappings(bockw_pinctrl_map, + ARRAY_SIZE(bockw_pinctrl_map)); + r8a7778_pinmux_init(); + + platform_device_register_resndata( + NULL, "sh_mmcif", -1, + mmc_resources, ARRAY_SIZE(mmc_resources), + &sh_mmcif_plat, sizeof(struct sh_mmcif_plat_data)); + + platform_device_register_resndata( + NULL, "rcar_usb_phy", -1, + usb_phy_resources, + ARRAY_SIZE(usb_phy_resources), + &usb_phy_platform_data, + sizeof(struct rcar_phy_platform_data)); + + regulator_register_fixed(0, dummy_supplies, + ARRAY_SIZE(dummy_supplies)); + regulator_register_always_on(1, "fixed-3.3V", fixed3v3_power_consumers, + ARRAY_SIZE(fixed3v3_power_consumers), 3300000); + + /* for SMSC */ + fpga = ioremap_nocache(FPGA, SZ_1M); + if (fpga) { + /* + * CAUTION + * + * IRQ0/1 is cascaded interrupt from FPGA. + * it should be cared in the future + * Now, it is assuming IRQ0 was used only from SMSC. + */ + u16 val = ioread16(fpga + IRQ0MR); + val &= ~(1 << 4); /* enable SMSC911x */ + iowrite16(val, fpga + IRQ0MR); + + platform_device_register_resndata( + NULL, "smsc911x", -1, + smsc911x_resources, ARRAY_SIZE(smsc911x_resources), + &smsc911x_data, sizeof(smsc911x_data)); + } + + /* for SDHI */ + base = ioremap_nocache(PFC, 0x200); + if (base) { + /* + * FIXME + * + * SDHI CD/WP pin needs pull-up + */ + iowrite32(ioread32(base + PUPR4) | (3 << 26), base + PUPR4); + iounmap(base); + + platform_device_register_resndata( + NULL, "sh_mobile_sdhi", 0, + sdhi0_resources, ARRAY_SIZE(sdhi0_resources), + &sdhi0_info, sizeof(struct tmio_mmc_data)); + } + + /* for Audio */ + rsnd_codec_power(5, 1); /* enable ak4642 */ + + platform_device_register_simple( + "ak4554-adc-dac", 0, NULL, 0); + + platform_device_register_simple( + "ak4554-adc-dac", 1, NULL, 0); + + pdev = platform_device_register_resndata( + NULL, "rcar_sound", -1, + rsnd_resources, ARRAY_SIZE(rsnd_resources), + &rsnd_info, sizeof(rsnd_info)); + + clk = clk_get(&pdev->dev, "clk_b"); + clk_set_rate(clk, 24576000); + clk_put(clk); + + for (i = 0; i < ARRAY_SIZE(rsnd_card_info); i++) { + struct platform_device_info cardinfo = { + .name = "asoc-simple-card", + .id = i, + .data = &rsnd_card_info[i], + .size_data = sizeof(struct asoc_simple_card_info), + .dma_mask = DMA_BIT_MASK(32), + }; + + platform_device_register_full(&cardinfo); + } +} + +static void __init bockw_init_late(void) +{ + r8a7778_init_late(); + ADD_USB_FUNC_DEVICE_IF_POSSIBLE(); +} + +static const char *bockw_boards_compat_dt[] __initdata = { + "renesas,bockw", + NULL, +}; + +DT_MACHINE_START(BOCKW_DT, "bockw") + .init_early = shmobile_init_delay, + .init_irq = r8a7778_init_irq_dt, + .init_machine = bockw_init, + .dt_compat = bockw_boards_compat_dt, + .init_late = bockw_init_late, +MACHINE_END diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c new file mode 100644 index 000000000..260d8319f --- /dev/null +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -0,0 +1,916 @@ +/* + * KZM-A9-GT board support + * + * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/i2c/pcf857x.h> +#include <linux/input.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sh_mmcif.h> +#include <linux/mmc/sh_mobile_sdhi.h> +#include <linux/mfd/as3711.h> +#include <linux/mfd/tmio.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/machine.h> +#include <linux/smsc911x.h> +#include <linux/usb/r8a66597.h> +#include <linux/usb/renesas_usbhs.h> +#include <linux/videodev2.h> + +#include <sound/sh_fsi.h> +#include <sound/simple_card.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <video/sh_mobile_lcdc.h> + +#include "common.h" +#include "intc.h" +#include "irqs.h" +#include "sh73a0.h" + +/* + * external GPIO + */ +#define GPIO_PCF8575_BASE (310) +#define GPIO_PCF8575_PORT10 (GPIO_PCF8575_BASE + 8) +#define GPIO_PCF8575_PORT11 (GPIO_PCF8575_BASE + 9) +#define GPIO_PCF8575_PORT12 (GPIO_PCF8575_BASE + 10) +#define GPIO_PCF8575_PORT13 (GPIO_PCF8575_BASE + 11) +#define GPIO_PCF8575_PORT14 (GPIO_PCF8575_BASE + 12) +#define GPIO_PCF8575_PORT15 (GPIO_PCF8575_BASE + 13) +#define GPIO_PCF8575_PORT16 (GPIO_PCF8575_BASE + 14) + +/* Dummy supplies, where voltage doesn't matter */ +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), + REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), +}; + +/* + * FSI-AK4648 + * + * this command is required when playback. + * + * # amixer set "LINEOUT Mixer DACL" on + */ + +/* SMSC 9221 */ +static struct resource smsc9221_resources[] = { + [0] = { + .start = 0x10000000, /* CS4 */ + .end = 0x100000ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = irq_pin(3), /* IRQ3 */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smsc911x_platform_config smsc9221_platdata = { + .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS, + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, +}; + +static struct platform_device smsc_device = { + .name = "smsc911x", + .dev = { + .platform_data = &smsc9221_platdata, + }, + .resource = smsc9221_resources, + .num_resources = ARRAY_SIZE(smsc9221_resources), +}; + +/* USB external chip */ +static struct r8a66597_platdata usb_host_data = { + .on_chip = 0, + .xtal = R8A66597_PLATDATA_XTAL_48MHZ, +}; + +static struct resource usb_resources[] = { + [0] = { + .start = 0x10010000, + .end = 0x1001ffff - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = irq_pin(1), /* IRQ1 */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usb_host_device = { + .name = "r8a66597_hcd", + .dev = { + .platform_data = &usb_host_data, + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(usb_resources), + .resource = usb_resources, +}; + +/* USB Func CN17 */ +struct usbhs_private { + void __iomem *phy; + void __iomem *cr2; + struct renesas_usbhs_platform_info info; +}; + +#define IRQ15 irq_pin(15) +#define USB_PHY_MODE (1 << 4) +#define USB_PHY_INT_EN ((1 << 3) | (1 << 2)) +#define USB_PHY_ON (1 << 1) +#define USB_PHY_OFF (1 << 0) +#define USB_PHY_INT_CLR (USB_PHY_ON | USB_PHY_OFF) + +#define usbhs_get_priv(pdev) \ + container_of(renesas_usbhs_get_info(pdev), struct usbhs_private, info) + +static int usbhs_get_vbus(struct platform_device *pdev) +{ + struct usbhs_private *priv = usbhs_get_priv(pdev); + + return !((1 << 7) & __raw_readw(priv->cr2)); +} + +static int usbhs_phy_reset(struct platform_device *pdev) +{ + struct usbhs_private *priv = usbhs_get_priv(pdev); + + /* init phy */ + __raw_writew(0x8a0a, priv->cr2); + + return 0; +} + +static int usbhs_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +static irqreturn_t usbhs_interrupt(int irq, void *data) +{ + struct platform_device *pdev = data; + struct usbhs_private *priv = usbhs_get_priv(pdev); + + renesas_usbhs_call_notify_hotplug(pdev); + + /* clear status */ + __raw_writew(__raw_readw(priv->phy) | USB_PHY_INT_CLR, priv->phy); + + return IRQ_HANDLED; +} + +static int usbhs_hardware_init(struct platform_device *pdev) +{ + struct usbhs_private *priv = usbhs_get_priv(pdev); + int ret; + + /* clear interrupt status */ + __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->phy); + + ret = request_irq(IRQ15, usbhs_interrupt, IRQF_TRIGGER_HIGH, + dev_name(&pdev->dev), pdev); + if (ret) { + dev_err(&pdev->dev, "request_irq err\n"); + return ret; + } + + /* enable USB phy interrupt */ + __raw_writew(USB_PHY_MODE | USB_PHY_INT_EN, priv->phy); + + return 0; +} + +static int usbhs_hardware_exit(struct platform_device *pdev) +{ + struct usbhs_private *priv = usbhs_get_priv(pdev); + + /* clear interrupt status */ + __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->phy); + + free_irq(IRQ15, pdev); + + return 0; +} + +static u32 usbhs_pipe_cfg[] = { + USB_ENDPOINT_XFER_CONTROL, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, +}; + +static struct usbhs_private usbhs_private = { + .phy = IOMEM(0xe60781e0), /* USBPHYINT */ + .cr2 = IOMEM(0xe605810c), /* USBCR2 */ + .info = { + .platform_callback = { + .hardware_init = usbhs_hardware_init, + .hardware_exit = usbhs_hardware_exit, + .get_id = usbhs_get_id, + .phy_reset = usbhs_phy_reset, + .get_vbus = usbhs_get_vbus, + }, + .driver_param = { + .buswait_bwait = 4, + .has_otg = 1, + .pipe_type = usbhs_pipe_cfg, + .pipe_size = ARRAY_SIZE(usbhs_pipe_cfg), + }, + }, +}; + +static struct resource usbhs_resources[] = { + [0] = { + .start = 0xE6890000, + .end = 0xE68900e6 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(62), + .end = gic_spi(62), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbhs_device = { + .name = "renesas_usbhs", + .id = -1, + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usbhs_private.info, + }, + .num_resources = ARRAY_SIZE(usbhs_resources), + .resource = usbhs_resources, +}; + +/* LCDC */ +static struct fb_videomode kzm_lcdc_mode = { + .name = "WVGA Panel", + .xres = 800, + .yres = 480, + .left_margin = 220, + .right_margin = 110, + .hsync_len = 70, + .upper_margin = 20, + .lower_margin = 5, + .vsync_len = 5, + .sync = 0, +}; + +static struct sh_mobile_lcdc_info lcdc_info = { + .clock_source = LCDC_CLK_BUS, + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, + .fourcc = V4L2_PIX_FMT_RGB565, + .interface_type = RGB24, + .lcd_modes = &kzm_lcdc_mode, + .num_modes = 1, + .clock_divider = 5, + .flags = 0, + .panel_cfg = { + .width = 152, + .height = 91, + }, + } +}; + +static struct resource lcdc_resources[] = { + [0] = { + .name = "LCDC", + .start = 0xfe940000, + .end = 0xfe943fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = intcs_evt2irq(0x580), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device lcdc_device = { + .name = "sh_mobile_lcdc_fb", + .num_resources = ARRAY_SIZE(lcdc_resources), + .resource = lcdc_resources, + .dev = { + .platform_data = &lcdc_info, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +/* Fixed 1.8V regulator to be used by MMCIF */ +static struct regulator_consumer_supply fixed1v8_power_consumers[] = +{ + REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"), + REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"), +}; + +/* MMCIF */ +static struct resource sh_mmcif_resources[] = { + [0] = { + .name = "MMCIF", + .start = 0xe6bd0000, + .end = 0xe6bd00ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(140), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = gic_spi(141), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct sh_mmcif_plat_data sh_mmcif_platdata = { + .ocr = MMC_VDD_165_195, + .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE, + .ccs_unsupported = true, + .slave_id_tx = SHDMA_SLAVE_MMCIF_TX, + .slave_id_rx = SHDMA_SLAVE_MMCIF_RX, +}; + +static struct platform_device mmc_device = { + .name = "sh_mmcif", + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + .platform_data = &sh_mmcif_platdata, + }, + .num_resources = ARRAY_SIZE(sh_mmcif_resources), + .resource = sh_mmcif_resources, +}; + +/* Fixed 3.3V regulators to be used by SDHI0 */ +static struct regulator_consumer_supply vcc_sdhi0_consumers[] = +{ + REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"), +}; + +static struct regulator_init_data vcc_sdhi0_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(vcc_sdhi0_consumers), + .consumer_supplies = vcc_sdhi0_consumers, +}; + +static struct fixed_voltage_config vcc_sdhi0_info = { + .supply_name = "SDHI0 Vcc", + .microvolts = 3300000, + .gpio = 15, + .enable_high = 1, + .init_data = &vcc_sdhi0_init_data, +}; + +static struct platform_device vcc_sdhi0 = { + .name = "reg-fixed-voltage", + .id = 0, + .dev = { + .platform_data = &vcc_sdhi0_info, + }, +}; + +/* Fixed 3.3V regulators to be used by SDHI2 */ +static struct regulator_consumer_supply vcc_sdhi2_consumers[] = +{ + REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.2"), +}; + +static struct regulator_init_data vcc_sdhi2_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(vcc_sdhi2_consumers), + .consumer_supplies = vcc_sdhi2_consumers, +}; + +static struct fixed_voltage_config vcc_sdhi2_info = { + .supply_name = "SDHI2 Vcc", + .microvolts = 3300000, + .gpio = 14, + .enable_high = 1, + .init_data = &vcc_sdhi2_init_data, +}; + +static struct platform_device vcc_sdhi2 = { + .name = "reg-fixed-voltage", + .id = 1, + .dev = { + .platform_data = &vcc_sdhi2_info, + }, +}; + +/* SDHI */ +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_HAS_IDLE_WAIT, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_POWER_OFF_CARD, +}; + +static struct resource sdhi0_resources[] = { + [0] = { + .name = "SDHI0", + .start = 0xee100000, + .end = 0xee1000ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = SH_MOBILE_SDHI_IRQ_CARD_DETECT, + .start = gic_spi(83), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = SH_MOBILE_SDHI_IRQ_SDCARD, + .start = gic_spi(84), + .flags = IORESOURCE_IRQ, + }, + [3] = { + .name = SH_MOBILE_SDHI_IRQ_SDIO, + .start = gic_spi(85), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sdhi0_device = { + .name = "sh_mobile_sdhi", + .num_resources = ARRAY_SIZE(sdhi0_resources), + .resource = sdhi0_resources, + .dev = { + .platform_data = &sdhi0_info, + }, +}; + +/* Micro SD */ +static struct tmio_mmc_data sdhi2_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI2_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI2_RX, + .flags = TMIO_MMC_HAS_IDLE_WAIT | + TMIO_MMC_USE_GPIO_CD | + TMIO_MMC_WRPROTECT_DISABLE, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD, + .cd_gpio = 13, +}; + +static struct resource sdhi2_resources[] = { + [0] = { + .name = "SDHI2", + .start = 0xee140000, + .end = 0xee1400ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = SH_MOBILE_SDHI_IRQ_CARD_DETECT, + .start = gic_spi(103), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = SH_MOBILE_SDHI_IRQ_SDCARD, + .start = gic_spi(104), + .flags = IORESOURCE_IRQ, + }, + [3] = { + .name = SH_MOBILE_SDHI_IRQ_SDIO, + .start = gic_spi(105), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sdhi2_device = { + .name = "sh_mobile_sdhi", + .id = 2, + .num_resources = ARRAY_SIZE(sdhi2_resources), + .resource = sdhi2_resources, + .dev = { + .platform_data = &sdhi2_info, + }, +}; + +/* KEY */ +#define GPIO_KEY(c, g, d) { .code = c, .gpio = g, .desc = d, .active_low = 1 } + +static struct gpio_keys_button gpio_buttons[] = { + GPIO_KEY(KEY_BACK, GPIO_PCF8575_PORT10, "SW3"), + GPIO_KEY(KEY_RIGHT, GPIO_PCF8575_PORT11, "SW2-R"), + GPIO_KEY(KEY_LEFT, GPIO_PCF8575_PORT12, "SW2-L"), + GPIO_KEY(KEY_ENTER, GPIO_PCF8575_PORT13, "SW2-P"), + GPIO_KEY(KEY_UP, GPIO_PCF8575_PORT14, "SW2-U"), + GPIO_KEY(KEY_DOWN, GPIO_PCF8575_PORT15, "SW2-D"), + GPIO_KEY(KEY_HOME, GPIO_PCF8575_PORT16, "SW1"), +}; + +static struct gpio_keys_platform_data gpio_key_info = { + .buttons = gpio_buttons, + .nbuttons = ARRAY_SIZE(gpio_buttons), +}; + +static struct platform_device gpio_keys_device = { + .name = "gpio-keys", + .dev = { + .platform_data = &gpio_key_info, + }, +}; + +/* FSI-AK4648 */ +static struct sh_fsi_platform_info fsi_info = { + .port_a = { + .tx_id = SHDMA_SLAVE_FSI2A_TX, + }, +}; + +static struct resource fsi_resources[] = { + [0] = { + .name = "FSI", + .start = 0xEC230000, + .end = 0xEC230400 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(146), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device fsi_device = { + .name = "sh_fsi2", + .id = -1, + .num_resources = ARRAY_SIZE(fsi_resources), + .resource = fsi_resources, + .dev = { + .platform_data = &fsi_info, + }, +}; + +static struct asoc_simple_card_info fsi2_ak4648_info = { + .name = "AK4648", + .card = "FSI2A-AK4648", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi2", + .daifmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM, + .cpu_dai = { + .name = "fsia-dai", + }, + .codec_dai = { + .name = "ak4642-hifi", + .sysclk = 11289600, + }, +}; + +static struct platform_device fsi_ak4648_device = { + .name = "asoc-simple-card", + .dev = { + .platform_data = &fsi2_ak4648_info, + .coherent_dma_mask = DMA_BIT_MASK(32), + .dma_mask = &fsi_ak4648_device.dev.coherent_dma_mask, + }, +}; + +/* I2C */ + +/* StepDown1 is used to supply 1.315V to the CPU */ +static struct regulator_init_data as3711_sd1 = { + .constraints = { + .name = "1.315V CPU", + .boot_on = 1, + .always_on = 1, + .min_uV = 1315000, + .max_uV = 1335000, + }, +}; + +/* StepDown2 is used to supply 1.8V to the CPU and to the board */ +static struct regulator_init_data as3711_sd2 = { + .constraints = { + .name = "1.8V", + .boot_on = 1, + .always_on = 1, + .min_uV = 1800000, + .max_uV = 1800000, + }, +}; + +/* + * StepDown3 is switched in parallel with StepDown2, seems to be off, + * according to read-back pre-set register values + */ + +/* StepDown4 is used to supply 1.215V to the CPU and to the board */ +static struct regulator_init_data as3711_sd4 = { + .constraints = { + .name = "1.215V", + .boot_on = 1, + .always_on = 1, + .min_uV = 1215000, + .max_uV = 1235000, + }, +}; + +/* LDO1 is unused and unconnected */ + +/* LDO2 is used to supply 2.8V to the CPU */ +static struct regulator_init_data as3711_ldo2 = { + .constraints = { + .name = "2.8V CPU", + .boot_on = 1, + .always_on = 1, + .min_uV = 2800000, + .max_uV = 2800000, + }, +}; + +/* LDO3 is used to supply 3.0V to the CPU */ +static struct regulator_init_data as3711_ldo3 = { + .constraints = { + .name = "3.0V CPU", + .boot_on = 1, + .always_on = 1, + .min_uV = 3000000, + .max_uV = 3000000, + }, +}; + +/* LDO4 is used to supply 2.8V to the board */ +static struct regulator_init_data as3711_ldo4 = { + .constraints = { + .name = "2.8V", + .boot_on = 1, + .always_on = 1, + .min_uV = 2800000, + .max_uV = 2800000, + }, +}; + +/* LDO5 is switched parallel to LDO4, also set to 2.8V */ +static struct regulator_init_data as3711_ldo5 = { + .constraints = { + .name = "2.8V #2", + .boot_on = 1, + .always_on = 1, + .min_uV = 2800000, + .max_uV = 2800000, + }, +}; + +/* LDO6 is unused and unconnected */ + +/* LDO7 is used to supply 1.15V to the CPU */ +static struct regulator_init_data as3711_ldo7 = { + .constraints = { + .name = "1.15V CPU", + .boot_on = 1, + .always_on = 1, + .min_uV = 1150000, + .max_uV = 1150000, + }, +}; + +/* LDO8 is switched parallel to LDO7, also set to 1.15V */ +static struct regulator_init_data as3711_ldo8 = { + .constraints = { + .name = "1.15V CPU #2", + .boot_on = 1, + .always_on = 1, + .min_uV = 1150000, + .max_uV = 1150000, + }, +}; + +static struct as3711_platform_data as3711_pdata = { + .regulator = { + .init_data = { + [AS3711_REGULATOR_SD_1] = &as3711_sd1, + [AS3711_REGULATOR_SD_2] = &as3711_sd2, + [AS3711_REGULATOR_SD_4] = &as3711_sd4, + [AS3711_REGULATOR_LDO_2] = &as3711_ldo2, + [AS3711_REGULATOR_LDO_3] = &as3711_ldo3, + [AS3711_REGULATOR_LDO_4] = &as3711_ldo4, + [AS3711_REGULATOR_LDO_5] = &as3711_ldo5, + [AS3711_REGULATOR_LDO_7] = &as3711_ldo7, + [AS3711_REGULATOR_LDO_8] = &as3711_ldo8, + }, + }, + .backlight = { + .su2_fb = "sh_mobile_lcdc_fb.0", + .su2_max_uA = 36000, + .su2_feedback = AS3711_SU2_CURR_AUTO, + .su2_fbprot = AS3711_SU2_GPIO4, + .su2_auto_curr1 = true, + .su2_auto_curr2 = true, + .su2_auto_curr3 = true, + }, +}; + +static struct pcf857x_platform_data pcf8575_pdata = { + .gpio_base = GPIO_PCF8575_BASE, +}; + +static struct i2c_board_info i2c0_devices[] = { + { + I2C_BOARD_INFO("ak4648", 0x12), + }, + { + I2C_BOARD_INFO("r2025sd", 0x32), + }, + { + I2C_BOARD_INFO("ak8975", 0x0c), + .irq = irq_pin(28), /* IRQ28 */ + }, + { + I2C_BOARD_INFO("adxl34x", 0x1d), + .irq = irq_pin(26), /* IRQ26 */ + }, + { + I2C_BOARD_INFO("as3711", 0x40), + .irq = intcs_evt2irq(0x3300), /* IRQ24 */ + .platform_data = &as3711_pdata, + }, +}; + +static struct i2c_board_info i2c1_devices[] = { + { + I2C_BOARD_INFO("st1232-ts", 0x55), + .irq = irq_pin(8), /* IRQ8 */ + }, +}; + +static struct i2c_board_info i2c3_devices[] = { + { + I2C_BOARD_INFO("pcf8575", 0x20), + .irq = irq_pin(19), /* IRQ19 */ + .platform_data = &pcf8575_pdata, + }, +}; + +static struct platform_device *kzm_devices[] __initdata = { + &smsc_device, + &usb_host_device, + &usbhs_device, + &lcdc_device, + &mmc_device, + &vcc_sdhi0, + &vcc_sdhi2, + &sdhi0_device, + &sdhi2_device, + &gpio_keys_device, + &fsi_device, + &fsi_ak4648_device, +}; + +static unsigned long pin_pullup_conf[] = { + PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP, 0), +}; + +static const struct pinctrl_map kzm_pinctrl_map[] = { + /* FSIA (AK4648) */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_fsi2", "pfc-sh73a0", + "fsia_mclk_in", "fsia"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_fsi2", "pfc-sh73a0", + "fsia_sclk_in", "fsia"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_fsi2", "pfc-sh73a0", + "fsia_data_in", "fsia"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_fsi2", "pfc-sh73a0", + "fsia_data_out", "fsia"), + /* I2C3 */ + PIN_MAP_MUX_GROUP_DEFAULT("i2c-sh_mobile.3", "pfc-sh73a0", + "i2c3_1", "i2c3"), + /* LCD */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "pfc-sh73a0", + "lcd_data24", "lcd"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "pfc-sh73a0", + "lcd_sync", "lcd"), + /* MMCIF */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh73a0", + "mmc0_data8_0", "mmc0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh73a0", + "mmc0_ctrl_0", "mmc0"), + PIN_MAP_CONFIGS_PIN_DEFAULT("sh_mmcif.0", "pfc-sh73a0", + "PORT279", pin_pullup_conf), + PIN_MAP_CONFIGS_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh73a0", + "mmc0_data8_0", pin_pullup_conf), + /* SCIFA4 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.4", "pfc-sh73a0", + "scifa4_data", "scifa4"), + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.4", "pfc-sh73a0", + "scifa4_ctrl", "scifa4"), + /* SDHI0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-sh73a0", + "sdhi0_data4", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-sh73a0", + "sdhi0_ctrl", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-sh73a0", + "sdhi0_cd", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-sh73a0", + "sdhi0_wp", "sdhi0"), + /* SDHI2 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-sh73a0", + "sdhi2_data4", "sdhi2"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-sh73a0", + "sdhi2_ctrl", "sdhi2"), + /* SMSC */ + PIN_MAP_MUX_GROUP_DEFAULT("smsc911x.0", "pfc-sh73a0", + "bsc_cs4", "bsc"), + /* USB */ + PIN_MAP_MUX_GROUP_DEFAULT("renesas_usbhs", "pfc-sh73a0", + "usb_vbus", "usb"), +}; + +static void __init kzm_init(void) +{ + regulator_register_always_on(2, "fixed-1.8V", fixed1v8_power_consumers, + ARRAY_SIZE(fixed1v8_power_consumers), 1800000); + regulator_register_fixed(3, dummy_supplies, ARRAY_SIZE(dummy_supplies)); + + pinctrl_register_mappings(kzm_pinctrl_map, ARRAY_SIZE(kzm_pinctrl_map)); + + sh73a0_pinmux_init(); + + /* SMSC */ + gpio_request_one(224, GPIOF_IN, NULL); /* IRQ3 */ + + /* LCDC */ + gpio_request_one(222, GPIOF_OUT_INIT_HIGH, NULL); /* LCDCDON */ + gpio_request_one(226, GPIOF_OUT_INIT_HIGH, NULL); /* SC */ + + /* Touchscreen */ + gpio_request_one(223, GPIOF_IN, NULL); /* IRQ8 */ + +#ifdef CONFIG_CACHE_L2X0 + /* Shared attribute override enable, 64K*8way */ + l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); +#endif + + i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); + i2c_register_board_info(1, i2c1_devices, ARRAY_SIZE(i2c1_devices)); + i2c_register_board_info(3, i2c3_devices, ARRAY_SIZE(i2c3_devices)); + + sh73a0_add_standard_devices(); + platform_add_devices(kzm_devices, ARRAY_SIZE(kzm_devices)); + + sh73a0_pm_init(); +} + +static void kzm9g_restart(enum reboot_mode mode, const char *cmd) +{ +#define RESCNT2 IOMEM(0xe6188020) + /* Do soft power on reset */ + writel((1 << 31), RESCNT2); +} + +static const char *kzm9g_boards_compat_dt[] __initdata = { + "renesas,kzm9g", + NULL, +}; + +DT_MACHINE_START(KZM9G_DT, "kzm9g") + .smp = smp_ops(sh73a0_smp_ops), + .map_io = sh73a0_map_io, + .init_early = sh73a0_add_early_devices, + .init_irq = sh73a0_init_irq, + .init_machine = kzm_init, + .init_late = shmobile_init_late, + .init_time = sh73a0_earlytimer_init, + .restart = kzm9g_restart, + .dt_compat = kzm9g_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/board-marzen-reference.c b/arch/arm/mach-shmobile/board-marzen-reference.c new file mode 100644 index 000000000..b15eb9232 --- /dev/null +++ b/arch/arm/mach-shmobile/board-marzen-reference.c @@ -0,0 +1,56 @@ +/* + * marzen board support - Reference DT implementation + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * Copyright (C) 2013 Simon Horman + * + * 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. + */ + +#include <linux/clk/shmobile.h> +#include <linux/clocksource.h> +#include <linux/of_platform.h> + +#include <asm/irq.h> +#include <asm/mach/arch.h> + +#include "common.h" +#include "irqs.h" +#include "r8a7779.h" + +static void __init marzen_init_timer(void) +{ + r8a7779_clocks_init(r8a7779_read_mode_pins()); + clocksource_of_init(); +} + +static void __init marzen_init(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + r8a7779_init_irq_extpin_dt(1); /* IRQ1 as individual interrupt */ +} + +static const char *marzen_boards_compat_dt[] __initdata = { + "renesas,marzen", + "renesas,marzen-reference", + NULL, +}; + +DT_MACHINE_START(MARZEN, "marzen") + .smp = smp_ops(r8a7779_smp_ops), + .map_io = r8a7779_map_io, + .init_early = shmobile_init_delay, + .init_time = marzen_init_timer, + .init_irq = r8a7779_init_irq_dt, + .init_machine = marzen_init, + .init_late = shmobile_init_late, + .dt_compat = marzen_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c new file mode 100644 index 000000000..51db288f1 --- /dev/null +++ b/arch/arm/mach-shmobile/board-marzen.c @@ -0,0 +1,347 @@ +/* + * marzen board support + * + * Copyright (C) 2011, 2013 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/leds.h> +#include <linux/dma-mapping.h> +#include <linux/pinctrl/machine.h> +#include <linux/platform_data/camera-rcar.h> +#include <linux/platform_data/gpio-rcar.h> +#include <linux/platform_data/usb-rcar-phy.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/machine.h> +#include <linux/smsc911x.h> +#include <linux/spi/spi.h> +#include <linux/spi/sh_hspi.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sh_mobile_sdhi.h> +#include <linux/mfd/tmio.h> + +#include <media/soc_camera.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/traps.h> + +#include "common.h" +#include "irqs.h" +#include "r8a7779.h" + +/* Fixed 3.3V regulator to be used by SDHI0 */ +static struct regulator_consumer_supply fixed3v3_power_consumers[] = { + REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"), + REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"), +}; + +/* Dummy supplies, where voltage doesn't matter */ +static struct regulator_consumer_supply dummy_supplies[] = { + REGULATOR_SUPPLY("vddvario", "smsc911x"), + REGULATOR_SUPPLY("vdd33a", "smsc911x"), +}; + +/* USB PHY */ +static struct resource usb_phy_resources[] = { + [0] = { + .start = 0xffe70800, + .end = 0xffe70900 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct rcar_phy_platform_data usb_phy_platform_data; + +static struct platform_device usb_phy = { + .name = "rcar_usb_phy", + .id = -1, + .dev = { + .platform_data = &usb_phy_platform_data, + }, + .resource = usb_phy_resources, + .num_resources = ARRAY_SIZE(usb_phy_resources), +}; + +/* SMSC LAN89218 */ +static struct resource smsc911x_resources[] = { + [0] = { + .start = 0x18000000, /* ExCS0 */ + .end = 0x180000ff, /* A1->A7 */ + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = irq_pin(1), /* IRQ 1 */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smsc911x_platform_config smsc911x_platdata = { + .flags = SMSC911X_USE_32BIT, /* 32-bit SW on 16-bit HW bus */ + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, +}; + +static struct platform_device eth_device = { + .name = "smsc911x", + .id = -1, + .dev = { + .platform_data = &smsc911x_platdata, + }, + .resource = smsc911x_resources, + .num_resources = ARRAY_SIZE(smsc911x_resources), +}; + +static struct resource sdhi0_resources[] = { + [0] = { + .name = "sdhi0", + .start = 0xffe4c000, + .end = 0xffe4c0ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_iid(0x88), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct tmio_mmc_data sdhi0_platform_data = { + .chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, + .capabilities = MMC_CAP_SD_HIGHSPEED, +}; + +static struct platform_device sdhi0_device = { + .name = "sh_mobile_sdhi", + .num_resources = ARRAY_SIZE(sdhi0_resources), + .resource = sdhi0_resources, + .id = 0, + .dev = { + .platform_data = &sdhi0_platform_data, + } +}; + +/* Thermal */ +static struct resource thermal_resources[] = { + [0] = { + .start = 0xFFC48000, + .end = 0xFFC48038 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device thermal_device = { + .name = "rcar_thermal", + .resource = thermal_resources, + .num_resources = ARRAY_SIZE(thermal_resources), +}; + +/* HSPI */ +static struct resource hspi_resources[] = { + [0] = { + .start = 0xFFFC7000, + .end = 0xFFFC7018 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device hspi_device = { + .name = "sh-hspi", + .id = 0, + .resource = hspi_resources, + .num_resources = ARRAY_SIZE(hspi_resources), +}; + +/* LEDS */ +static struct gpio_led marzen_leds[] = { + { + .name = "led2", + .gpio = RCAR_GP_PIN(4, 29), + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, { + .name = "led3", + .gpio = RCAR_GP_PIN(4, 30), + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, { + .name = "led4", + .gpio = RCAR_GP_PIN(4, 31), + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, +}; + +static struct gpio_led_platform_data marzen_leds_pdata = { + .leds = marzen_leds, + .num_leds = ARRAY_SIZE(marzen_leds), +}; + +static struct platform_device leds_device = { + .name = "leds-gpio", + .id = 0, + .dev = { + .platform_data = &marzen_leds_pdata, + }, +}; + +/* VIN */ +static struct rcar_vin_platform_data vin_platform_data __initdata = { + .flags = RCAR_VIN_BT656, +}; + +#define MARZEN_VIN(idx) \ +static struct resource vin##idx##_resources[] __initdata = { \ + DEFINE_RES_MEM(0xffc50000 + 0x1000 * (idx), 0x1000), \ + DEFINE_RES_IRQ(gic_iid(0x5f + (idx))), \ +}; \ + \ +static struct platform_device_info vin##idx##_info __initdata = { \ + .name = "r8a7779-vin", \ + .id = idx, \ + .res = vin##idx##_resources, \ + .num_res = ARRAY_SIZE(vin##idx##_resources), \ + .dma_mask = DMA_BIT_MASK(32), \ + .data = &vin_platform_data, \ + .size_data = sizeof(vin_platform_data), \ +} +MARZEN_VIN(1); +MARZEN_VIN(3); + +#define MARZEN_CAMERA(idx) \ +static struct i2c_board_info camera##idx##_info = { \ + I2C_BOARD_INFO("adv7180", 0x20 + (idx)), \ +}; \ + \ +static struct soc_camera_link iclink##idx##_adv7180 = { \ + .bus_id = 1 + 2 * (idx), \ + .i2c_adapter_id = 0, \ + .board_info = &camera##idx##_info, \ +}; \ + \ +static struct platform_device camera##idx##_device = { \ + .name = "soc-camera-pdrv", \ + .id = idx, \ + .dev = { \ + .platform_data = &iclink##idx##_adv7180, \ + }, \ +}; + +MARZEN_CAMERA(0); +MARZEN_CAMERA(1); + +static struct platform_device *marzen_devices[] __initdata = { + ð_device, + &sdhi0_device, + &thermal_device, + &hspi_device, + &leds_device, + &usb_phy, + &camera0_device, + &camera1_device, +}; + +static const struct pinctrl_map marzen_pinctrl_map[] = { + /* DU (CN10: ARGB0, CN13: LVDS) */ + PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7779", "pfc-r8a7779", + "du0_rgb888", "du0"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7779", "pfc-r8a7779", + "du0_sync_1", "du0"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7779", "pfc-r8a7779", + "du0_clk_out_0", "du0"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7779", "pfc-r8a7779", + "du1_rgb666", "du1"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7779", "pfc-r8a7779", + "du1_sync_1", "du1"), + PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7779", "pfc-r8a7779", + "du1_clk_out", "du1"), + /* HSPI0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-hspi.0", "pfc-r8a7779", + "hspi0", "hspi0"), + /* SCIF2 (CN18: DEBUG0) */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.2", "pfc-r8a7779", + "scif2_data_c", "scif2"), + /* SCIF4 (CN19: DEBUG1) */ + PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.4", "pfc-r8a7779", + "scif4_data", "scif4"), + /* SDHI0 */ + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7779", + "sdhi0_data4", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7779", + "sdhi0_ctrl", "sdhi0"), + PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7779", + "sdhi0_cd", "sdhi0"), + /* SMSC */ + PIN_MAP_MUX_GROUP_DEFAULT("smsc911x", "pfc-r8a7779", + "intc_irq1_b", "intc"), + PIN_MAP_MUX_GROUP_DEFAULT("smsc911x", "pfc-r8a7779", + "lbsc_ex_cs0", "lbsc"), + /* USB0 */ + PIN_MAP_MUX_GROUP_DEFAULT("ehci-platform.0", "pfc-r8a7779", + "usb0", "usb0"), + /* USB1 */ + PIN_MAP_MUX_GROUP_DEFAULT("ehci-platform.0", "pfc-r8a7779", + "usb1", "usb1"), + /* USB2 */ + PIN_MAP_MUX_GROUP_DEFAULT("ehci-platform.1", "pfc-r8a7779", + "usb2", "usb2"), + /* VIN1 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.1", "pfc-r8a7779", + "vin1_clk", "vin1"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.1", "pfc-r8a7779", + "vin1_data8", "vin1"), + /* VIN3 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.3", "pfc-r8a7779", + "vin3_clk", "vin3"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.3", "pfc-r8a7779", + "vin3_data8", "vin3"), +}; + +static void __init marzen_init(void) +{ + regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers, + ARRAY_SIZE(fixed3v3_power_consumers), 3300000); + regulator_register_fixed(1, dummy_supplies, + ARRAY_SIZE(dummy_supplies)); + + pinctrl_register_mappings(marzen_pinctrl_map, + ARRAY_SIZE(marzen_pinctrl_map)); + r8a7779_pinmux_init(); + r8a7779_init_irq_extpin(1); /* IRQ1 as individual interrupt */ + + r8a7779_add_standard_devices(); + platform_device_register_full(&vin1_info); + platform_device_register_full(&vin3_info); + platform_add_devices(marzen_devices, ARRAY_SIZE(marzen_devices)); +} + +static const char *marzen_boards_compat_dt[] __initdata = { + "renesas,marzen", + NULL, +}; + +DT_MACHINE_START(MARZEN, "marzen") + .smp = smp_ops(r8a7779_smp_ops), + .map_io = r8a7779_map_io, + .init_early = r8a7779_add_early_devices, + .init_irq = r8a7779_init_irq_dt, + .init_machine = marzen_init, + .init_late = r8a7779_init_late, + .dt_compat = marzen_boards_compat_dt, + .init_time = r8a7779_earlytimer_init, +MACHINE_END diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c new file mode 100644 index 000000000..9cac8247c --- /dev/null +++ b/arch/arm/mach-shmobile/clock-r8a7740.c @@ -0,0 +1,675 @@ +/* + * R8A7740 processor support + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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; either 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. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/sh_clk.h> +#include <linux/clkdev.h> + +#include "clock.h" +#include "common.h" +#include "r8a7740.h" + +/* + * | MDx | XTAL1/EXTAL1 | System | EXTALR | + * Clock |-------+-----------------+ clock | 32.768 | RCLK + * Mode | 2/1/0 | src MHz | source | KHz | source + * -------+-------+-----------------+-----------+--------+---------- + * 0 | 0 0 0 | External 20~50 | XTAL1 | O | EXTALR + * 1 | 0 0 1 | Crystal 20~30 | XTAL1 | O | EXTALR + * 2 | 0 1 0 | External 40~50 | XTAL1 / 2 | O | EXTALR + * 3 | 0 1 1 | Crystal 40~50 | XTAL1 / 2 | O | EXTALR + * 4 | 1 0 0 | External 20~50 | XTAL1 | x | XTAL1 / 1024 + * 5 | 1 0 1 | Crystal 20~30 | XTAL1 | x | XTAL1 / 1024 + * 6 | 1 1 0 | External 40~50 | XTAL1 / 2 | x | XTAL1 / 2048 + * 7 | 1 1 1 | Crystal 40~50 | XTAL1 / 2 | x | XTAL1 / 2048 + */ + +/* CPG registers */ +#define FRQCRA IOMEM(0xe6150000) +#define FRQCRB IOMEM(0xe6150004) +#define VCLKCR1 IOMEM(0xE6150008) +#define VCLKCR2 IOMEM(0xE615000c) +#define FRQCRC IOMEM(0xe61500e0) +#define FSIACKCR IOMEM(0xe6150018) +#define PLLC01CR IOMEM(0xe6150028) + +#define SUBCKCR IOMEM(0xe6150080) +#define USBCKCR IOMEM(0xe615008c) + +#define MSTPSR0 IOMEM(0xe6150030) +#define MSTPSR1 IOMEM(0xe6150038) +#define MSTPSR2 IOMEM(0xe6150040) +#define MSTPSR3 IOMEM(0xe6150048) +#define MSTPSR4 IOMEM(0xe615004c) +#define FSIBCKCR IOMEM(0xe6150090) +#define HDMICKCR IOMEM(0xe6150094) +#define SMSTPCR0 IOMEM(0xe6150130) +#define SMSTPCR1 IOMEM(0xe6150134) +#define SMSTPCR2 IOMEM(0xe6150138) +#define SMSTPCR3 IOMEM(0xe615013c) +#define SMSTPCR4 IOMEM(0xe6150140) + +#define FSIDIVA IOMEM(0xFE1F8000) +#define FSIDIVB IOMEM(0xFE1F8008) + +/* Fixed 32 KHz root clock from EXTALR pin */ +static struct clk extalr_clk = { + .rate = 32768, +}; + +/* + * 25MHz default rate for the EXTAL1 root input clock. + * If needed, reset this with clk_set_rate() from the platform code. + */ +static struct clk extal1_clk = { + .rate = 25000000, +}; + +/* + * 48MHz default rate for the EXTAL2 root input clock. + * If needed, reset this with clk_set_rate() from the platform code. + */ +static struct clk extal2_clk = { + .rate = 48000000, +}; + +/* + * 27MHz default rate for the DV_CLKI root input clock. + * If needed, reset this with clk_set_rate() from the platform code. + */ +static struct clk dv_clk = { + .rate = 27000000, +}; + +SH_CLK_RATIO(div2, 1, 2); +SH_CLK_RATIO(div1k, 1, 1024); + +SH_FIXED_RATIO_CLK(extal1_div2_clk, extal1_clk, div2); +SH_FIXED_RATIO_CLK(extal1_div1024_clk, extal1_clk, div1k); +SH_FIXED_RATIO_CLK(extal1_div2048_clk, extal1_div2_clk, div1k); +SH_FIXED_RATIO_CLK(extal2_div2_clk, extal2_clk, div2); + +static struct sh_clk_ops followparent_clk_ops = { + .recalc = followparent_recalc, +}; + +/* Main clock */ +static struct clk system_clk = { + .ops = &followparent_clk_ops, +}; + +SH_FIXED_RATIO_CLK(system_div2_clk, system_clk, div2); + +/* r_clk */ +static struct clk r_clk = { + .ops = &followparent_clk_ops, +}; + +/* PLLC0/PLLC1 */ +static unsigned long pllc01_recalc(struct clk *clk) +{ + unsigned long mult = 1; + + if (__raw_readl(PLLC01CR) & (1 << 14)) + mult = ((__raw_readl(clk->enable_reg) >> 24) & 0x7f) + 1; + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops pllc01_clk_ops = { + .recalc = pllc01_recalc, +}; + +static struct clk pllc0_clk = { + .ops = &pllc01_clk_ops, + .flags = CLK_ENABLE_ON_INIT, + .parent = &system_clk, + .enable_reg = (void __iomem *)FRQCRC, +}; + +static struct clk pllc1_clk = { + .ops = &pllc01_clk_ops, + .flags = CLK_ENABLE_ON_INIT, + .parent = &system_div2_clk, + .enable_reg = (void __iomem *)FRQCRA, +}; + +/* PLLC1 / 2 */ +SH_FIXED_RATIO_CLK(pllc1_div2_clk, pllc1_clk, div2); + +/* USB clock */ +/* + * USBCKCR is controlling usb24 clock + * bit[7] : parent clock + * bit[6] : clock divide rate + * And this bit[7] is used as a "usb24s" from other devices. + * (Video clock / Sub clock / SPU clock) + * You can controll this clock as a below. + * + * struct clk *usb24 = clk_get(dev, "usb24"); + * struct clk *usb24s = clk_get(NULL, "usb24s"); + * struct clk *system = clk_get(NULL, "system_clk"); + * int rate = clk_get_rate(system); + * + * clk_set_parent(usb24s, system); // for bit[7] + * clk_set_rate(usb24, rate / 2); // for bit[6] + */ +static struct clk *usb24s_parents[] = { + [0] = &system_clk, + [1] = &extal2_clk +}; + +static int usb24s_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(USBCKCR) & ~(1 << 8), USBCKCR); + + return 0; +} + +static void usb24s_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(USBCKCR) | (1 << 8), USBCKCR); +} + +static int usb24s_set_parent(struct clk *clk, struct clk *parent) +{ + int i, ret; + u32 val; + + if (!clk->parent_table || !clk->parent_num) + return -EINVAL; + + /* Search the parent */ + for (i = 0; i < clk->parent_num; i++) + if (clk->parent_table[i] == parent) + break; + + if (i == clk->parent_num) + return -ENODEV; + + ret = clk_reparent(clk, parent); + if (ret < 0) + return ret; + + val = __raw_readl(USBCKCR); + val &= ~(1 << 7); + val |= i << 7; + __raw_writel(val, USBCKCR); + + return 0; +} + +static struct sh_clk_ops usb24s_clk_ops = { + .recalc = followparent_recalc, + .enable = usb24s_enable, + .disable = usb24s_disable, + .set_parent = usb24s_set_parent, +}; + +static struct clk usb24s_clk = { + .ops = &usb24s_clk_ops, + .parent_table = usb24s_parents, + .parent_num = ARRAY_SIZE(usb24s_parents), + .parent = &system_clk, +}; + +static unsigned long usb24_recalc(struct clk *clk) +{ + return clk->parent->rate / + ((__raw_readl(USBCKCR) & (1 << 6)) ? 1 : 2); +}; + +static int usb24_set_rate(struct clk *clk, unsigned long rate) +{ + u32 val; + + /* closer to which ? parent->rate or parent->rate/2 */ + val = __raw_readl(USBCKCR); + val &= ~(1 << 6); + val |= (rate > (clk->parent->rate / 4) * 3) << 6; + __raw_writel(val, USBCKCR); + + return 0; +} + +static struct sh_clk_ops usb24_clk_ops = { + .recalc = usb24_recalc, + .set_rate = usb24_set_rate, +}; + +static struct clk usb24_clk = { + .ops = &usb24_clk_ops, + .parent = &usb24s_clk, +}; + +/* External FSIACK/FSIBCK clock */ +static struct clk fsiack_clk = { +}; + +static struct clk fsibck_clk = { +}; + +static struct clk *main_clks[] = { + &extalr_clk, + &extal1_clk, + &extal2_clk, + &extal1_div2_clk, + &extal1_div1024_clk, + &extal1_div2048_clk, + &extal2_div2_clk, + &dv_clk, + &system_clk, + &system_div2_clk, + &r_clk, + &pllc0_clk, + &pllc1_clk, + &pllc1_div2_clk, + &usb24s_clk, + &usb24_clk, + &fsiack_clk, + &fsibck_clk, +}; + +/* DIV4 clocks */ +static void div4_kick(struct clk *clk) +{ + unsigned long value; + + /* set KICK bit in FRQCRB to update hardware setting */ + value = __raw_readl(FRQCRB); + value |= (1 << 31); + __raw_writel(value, FRQCRB); +} + +static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, + 24, 32, 36, 48, 0, 72, 96, 0 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, + .kick = div4_kick, +}; + +enum { + DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_HP, + DIV4_HPP, DIV4_USBP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP, + DIV4_NR +}; + +static struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 20, 0x6fff, CLK_ENABLE_ON_INIT), + [DIV4_ZG] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 16, 0x6fff, CLK_ENABLE_ON_INIT), + [DIV4_B] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 8, 0x6fff, CLK_ENABLE_ON_INIT), + [DIV4_M1] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT), + [DIV4_HP] = SH_CLK_DIV4(&pllc1_clk, FRQCRB, 4, 0x6fff, 0), + [DIV4_HPP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 20, 0x6fff, 0), + [DIV4_USBP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 16, 0x6fff, 0), + [DIV4_S] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 12, 0x6fff, 0), + [DIV4_ZB] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 8, 0x6fff, 0), + [DIV4_M3] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 4, 0x6fff, 0), + [DIV4_CP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 0, 0x6fff, 0), +}; + +/* DIV6 reparent */ +enum { + DIV6_HDMI, + DIV6_VCLK1, DIV6_VCLK2, + DIV6_FSIA, DIV6_FSIB, + DIV6_REPARENT_NR, +}; + +static struct clk *hdmi_parent[] = { + [0] = &pllc1_div2_clk, + [1] = &system_clk, + [2] = &dv_clk +}; + +static struct clk *vclk_parents[8] = { + [0] = &pllc1_div2_clk, + [2] = &dv_clk, + [3] = &usb24s_clk, + [4] = &extal1_div2_clk, + [5] = &extalr_clk, +}; + +static struct clk *fsia_parents[] = { + [0] = &pllc1_div2_clk, + [1] = &fsiack_clk, /* external clock */ +}; + +static struct clk *fsib_parents[] = { + [0] = &pllc1_div2_clk, + [1] = &fsibck_clk, /* external clock */ +}; + +static struct clk div6_reparent_clks[DIV6_REPARENT_NR] = { + [DIV6_HDMI] = SH_CLK_DIV6_EXT(HDMICKCR, 0, + hdmi_parent, ARRAY_SIZE(hdmi_parent), 6, 2), + [DIV6_VCLK1] = SH_CLK_DIV6_EXT(VCLKCR1, 0, + vclk_parents, ARRAY_SIZE(vclk_parents), 12, 3), + [DIV6_VCLK2] = SH_CLK_DIV6_EXT(VCLKCR2, 0, + vclk_parents, ARRAY_SIZE(vclk_parents), 12, 3), + [DIV6_FSIA] = SH_CLK_DIV6_EXT(FSIACKCR, 0, + fsia_parents, ARRAY_SIZE(fsia_parents), 6, 2), + [DIV6_FSIB] = SH_CLK_DIV6_EXT(FSIBCKCR, 0, + fsib_parents, ARRAY_SIZE(fsib_parents), 6, 2), +}; + +/* DIV6 clocks */ +enum { + DIV6_SUB, + DIV6_NR +}; + +static struct clk div6_clks[DIV6_NR] = { + [DIV6_SUB] = SH_CLK_DIV6(&pllc1_div2_clk, SUBCKCR, 0), +}; + +/* HDMI1/2 clock */ +static unsigned long hdmi12_recalc(struct clk *clk) +{ + u32 val = __raw_readl(HDMICKCR); + int shift = (int)clk->priv; + + val >>= shift; + val &= 0x3; + + return clk->parent->rate / (1 << val); +}; + +static int hdmi12_set_rate(struct clk *clk, unsigned long rate) +{ + u32 val, mask; + int i, shift; + + for (i = 0; i < 3; i++) + if (rate == clk->parent->rate / (1 << i)) + goto find; + return -ENODEV; + +find: + shift = (int)clk->priv; + + val = __raw_readl(HDMICKCR); + mask = ~(0x3 << shift); + val = (val & mask) | i << shift; + __raw_writel(val, HDMICKCR); + + return 0; +}; + +static struct sh_clk_ops hdmi12_clk_ops = { + .recalc = hdmi12_recalc, + .set_rate = hdmi12_set_rate, +}; + +static struct clk hdmi1_clk = { + .ops = &hdmi12_clk_ops, + .priv = (void *)9, + .parent = &div6_reparent_clks[DIV6_HDMI], /* late install */ +}; + +static struct clk hdmi2_clk = { + .ops = &hdmi12_clk_ops, + .priv = (void *)11, + .parent = &div6_reparent_clks[DIV6_HDMI], /* late install */ +}; + +static struct clk *late_main_clks[] = { + &hdmi1_clk, + &hdmi2_clk, +}; + +/* FSI DIV */ +enum { FSIDIV_A, FSIDIV_B, FSIDIV_REPARENT_NR }; + +static struct clk fsidivs[] = { + [FSIDIV_A] = SH_CLK_FSIDIV(FSIDIVA, &div6_reparent_clks[DIV6_FSIA]), + [FSIDIV_B] = SH_CLK_FSIDIV(FSIDIVB, &div6_reparent_clks[DIV6_FSIB]), +}; + +/* MSTP */ +enum { + MSTP128, MSTP127, MSTP125, + MSTP116, MSTP111, MSTP100, MSTP117, + + MSTP230, MSTP229, + MSTP222, + MSTP218, MSTP217, MSTP216, MSTP214, + MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, + + MSTP329, MSTP328, MSTP323, MSTP320, + MSTP314, MSTP313, MSTP312, + MSTP309, MSTP304, + + MSTP416, MSTP415, MSTP407, MSTP406, + + MSTP_NR +}; + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP128] = SH_CLK_MSTP32(&div4_clks[DIV4_S], SMSTPCR1, 28, 0), /* CEU21 */ + [MSTP127] = SH_CLK_MSTP32(&div4_clks[DIV4_S], SMSTPCR1, 27, 0), /* CEU20 */ + [MSTP125] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */ + [MSTP117] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 17, 0), /* LCDC1 */ + [MSTP116] = SH_CLK_MSTP32(&div4_clks[DIV4_HPP], SMSTPCR1, 16, 0), /* IIC0 */ + [MSTP111] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 11, 0), /* TMU1 */ + [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */ + + [MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */ + [MSTP229] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 29, 0), /* INTCA */ + [MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */ + [MSTP218] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* DMAC1 */ + [MSTP217] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 17, 0), /* DMAC2 */ + [MSTP216] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 16, 0), /* DMAC3 */ + [MSTP214] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 14, 0), /* USBDMAC */ + [MSTP207] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 7, 0), /* SCIFA5 */ + [MSTP206] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 6, 0), /* SCIFB */ + [MSTP204] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 4, 0), /* SCIFA0 */ + [MSTP203] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 3, 0), /* SCIFA1 */ + [MSTP202] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 2, 0), /* SCIFA2 */ + [MSTP201] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 1, 0), /* SCIFA3 */ + [MSTP200] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 0, 0), /* SCIFA4 */ + + [MSTP329] = SH_CLK_MSTP32(&r_clk, SMSTPCR3, 29, 0), /* CMT10 */ + [MSTP328] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 28, 0), /* FSI */ + [MSTP323] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */ + [MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 20, 0), /* USBF */ + [MSTP314] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 14, 0), /* SDHI0 */ + [MSTP313] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */ + [MSTP312] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */ + [MSTP309] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 9, 0), /* GEther */ + [MSTP304] = SH_CLK_MSTP32(&div4_clks[DIV4_CP], SMSTPCR3, 4, 0), /* TPU0 */ + + [MSTP416] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 16, 0), /* USBHOST */ + [MSTP415] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 15, 0), /* SDHI2 */ + [MSTP407] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 7, 0), /* USB-Func */ + [MSTP406] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 6, 0), /* USB Phy */ +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("extalr", &extalr_clk), + CLKDEV_CON_ID("extal1", &extal1_clk), + CLKDEV_CON_ID("extal2", &extal2_clk), + CLKDEV_CON_ID("extal1_div2", &extal1_div2_clk), + CLKDEV_CON_ID("extal1_div1024", &extal1_div1024_clk), + CLKDEV_CON_ID("extal1_div2048", &extal1_div2048_clk), + CLKDEV_CON_ID("extal2_div2", &extal2_div2_clk), + CLKDEV_CON_ID("dv_clk", &dv_clk), + CLKDEV_CON_ID("system_clk", &system_clk), + CLKDEV_CON_ID("system_div2_clk", &system_div2_clk), + CLKDEV_CON_ID("r_clk", &r_clk), + CLKDEV_CON_ID("pllc0_clk", &pllc0_clk), + CLKDEV_CON_ID("pllc1_clk", &pllc1_clk), + CLKDEV_CON_ID("pllc1_div2_clk", &pllc1_div2_clk), + CLKDEV_CON_ID("usb24s", &usb24s_clk), + CLKDEV_CON_ID("hdmi1", &hdmi1_clk), + CLKDEV_CON_ID("hdmi2", &hdmi2_clk), + CLKDEV_CON_ID("video1", &div6_reparent_clks[DIV6_VCLK1]), + CLKDEV_CON_ID("video2", &div6_reparent_clks[DIV6_VCLK2]), + CLKDEV_CON_ID("fsiack", &fsiack_clk), + CLKDEV_CON_ID("fsibck", &fsibck_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("i_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("zg_clk", &div4_clks[DIV4_ZG]), + CLKDEV_CON_ID("b_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("m1_clk", &div4_clks[DIV4_M1]), + CLKDEV_CON_ID("hp_clk", &div4_clks[DIV4_HP]), + CLKDEV_CON_ID("hpp_clk", &div4_clks[DIV4_HPP]), + CLKDEV_CON_ID("s_clk", &div4_clks[DIV4_S]), + CLKDEV_CON_ID("zb_clk", &div4_clks[DIV4_ZB]), + CLKDEV_CON_ID("m3_clk", &div4_clks[DIV4_M3]), + CLKDEV_CON_ID("cp_clk", &div4_clks[DIV4_CP]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("sub_clk", &div6_clks[DIV6_SUB]), + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]), + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP116]), + CLKDEV_DEV_ID("fff20000.i2c", &mstp_clks[MSTP116]), + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.1", &mstp_clks[MSTP117]), + CLKDEV_DEV_ID("sh_mobile_ceu.0", &mstp_clks[MSTP127]), + CLKDEV_DEV_ID("sh_mobile_ceu.1", &mstp_clks[MSTP128]), + + CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP200]), + CLKDEV_DEV_ID("e6c80000.serial", &mstp_clks[MSTP200]), + CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP201]), + CLKDEV_DEV_ID("e6c70000.serial", &mstp_clks[MSTP201]), + CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP202]), + CLKDEV_DEV_ID("e6c60000.serial", &mstp_clks[MSTP202]), + CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), + CLKDEV_DEV_ID("e6c50000.serial", &mstp_clks[MSTP203]), + CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), + CLKDEV_DEV_ID("e6c40000.serial", &mstp_clks[MSTP204]), + CLKDEV_DEV_ID("sh-sci.8", &mstp_clks[MSTP206]), + CLKDEV_DEV_ID("e6c30000.serial", &mstp_clks[MSTP206]), + CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), + CLKDEV_DEV_ID("e6cb0000.serial", &mstp_clks[MSTP207]), + CLKDEV_DEV_ID("sh-dma-engine.3", &mstp_clks[MSTP214]), + CLKDEV_DEV_ID("sh-dma-engine.2", &mstp_clks[MSTP216]), + CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[MSTP217]), + CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[MSTP218]), + CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP222]), + CLKDEV_DEV_ID("e6cd0000.serial", &mstp_clks[MSTP222]), + CLKDEV_DEV_ID("renesas_intc_irqpin.0", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("renesas_intc_irqpin.1", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("renesas_intc_irqpin.2", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("renesas_intc_irqpin.3", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP230]), + CLKDEV_DEV_ID("e6cc0000.serial", &mstp_clks[MSTP230]), + + CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]), + CLKDEV_DEV_ID("fe1f0000.sound", &mstp_clks[MSTP328]), + CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]), + CLKDEV_DEV_ID("e6c20000.i2c", &mstp_clks[MSTP323]), + CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP320]), + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), + CLKDEV_DEV_ID("e6850000.sd", &mstp_clks[MSTP314]), + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), + CLKDEV_DEV_ID("e6860000.sd", &mstp_clks[MSTP313]), + CLKDEV_DEV_ID("sh_mmcif", &mstp_clks[MSTP312]), + CLKDEV_DEV_ID("e6bd0000.mmc", &mstp_clks[MSTP312]), + CLKDEV_DEV_ID("r8a7740-gether", &mstp_clks[MSTP309]), + CLKDEV_DEV_ID("e9a00000.ethernet", &mstp_clks[MSTP309]), + CLKDEV_DEV_ID("renesas-tpu-pwm", &mstp_clks[MSTP304]), + CLKDEV_DEV_ID("e6600000.pwm", &mstp_clks[MSTP304]), + + CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP415]), + CLKDEV_DEV_ID("e6870000.sd", &mstp_clks[MSTP415]), + + /* ICK */ + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP111]), + CLKDEV_ICK_ID("fck", "fff90000.timer", &mstp_clks[MSTP111]), + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP125]), + CLKDEV_ICK_ID("fck", "fff80000.timer", &mstp_clks[MSTP125]), + CLKDEV_ICK_ID("fck", "sh-cmt-48.1", &mstp_clks[MSTP329]), + CLKDEV_ICK_ID("fck", "e6138000.timer", &mstp_clks[MSTP329]), + CLKDEV_ICK_ID("host", "renesas_usbhs", &mstp_clks[MSTP416]), + CLKDEV_ICK_ID("func", "renesas_usbhs", &mstp_clks[MSTP407]), + CLKDEV_ICK_ID("phy", "renesas_usbhs", &mstp_clks[MSTP406]), + CLKDEV_ICK_ID("pci", "renesas_usbhs", &div4_clks[DIV4_USBP]), + CLKDEV_ICK_ID("usb24", "renesas_usbhs", &usb24_clk), + CLKDEV_ICK_ID("ick", "sh-mobile-hdmi", &div6_reparent_clks[DIV6_HDMI]), + + CLKDEV_ICK_ID("icka", "sh_fsi2", &div6_reparent_clks[DIV6_FSIA]), + CLKDEV_ICK_ID("ickb", "sh_fsi2", &div6_reparent_clks[DIV6_FSIB]), + CLKDEV_ICK_ID("diva", "sh_fsi2", &fsidivs[FSIDIV_A]), + CLKDEV_ICK_ID("divb", "sh_fsi2", &fsidivs[FSIDIV_B]), + CLKDEV_ICK_ID("xcka", "sh_fsi2", &fsiack_clk), + CLKDEV_ICK_ID("xckb", "sh_fsi2", &fsibck_clk), +}; + +void __init r8a7740_clock_init(u8 md_ck) +{ + int k, ret = 0; + + /* detect system clock parent */ + if (md_ck & MD_CK1) + system_clk.parent = &extal1_div2_clk; + else + system_clk.parent = &extal1_clk; + + /* detect RCLK parent */ + switch (md_ck & (MD_CK2 | MD_CK1)) { + case MD_CK2 | MD_CK1: + r_clk.parent = &extal1_div2048_clk; + break; + case MD_CK2: + r_clk.parent = &extal1_div1024_clk; + break; + case MD_CK1: + default: + r_clk.parent = &extalr_clk; + break; + } + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_div6_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_div6_reparent_register(div6_reparent_clks, + DIV6_REPARENT_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++) + ret = clk_register(late_main_clks[k]); + + if (!ret) + ret = sh_clk_fsidiv_register(fsidivs, FSIDIV_REPARENT_NR); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + shmobile_clk_init(); + else + panic("failed to setup r8a7740 clocks\n"); +} diff --git a/arch/arm/mach-shmobile/clock-r8a7778.c b/arch/arm/mach-shmobile/clock-r8a7778.c new file mode 100644 index 000000000..e8510c355 --- /dev/null +++ b/arch/arm/mach-shmobile/clock-r8a7778.c @@ -0,0 +1,342 @@ +/* + * r8a7778 clock framework support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on r8a7779 + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 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; either 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. + */ + +/* + * MD MD MD MD PLLA PLLB EXTAL clki clkz + * 19 18 12 11 (HMz) (MHz) (MHz) + *---------------------------------------------------------------------------- + * 1 0 0 0 x21 x21 38.00 800 800 + * 1 0 0 1 x24 x24 33.33 800 800 + * 1 0 1 0 x28 x28 28.50 800 800 + * 1 0 1 1 x32 x32 25.00 800 800 + * 1 1 0 1 x24 x21 33.33 800 700 + * 1 1 1 0 x28 x21 28.50 800 600 + * 1 1 1 1 x32 x24 25.00 800 600 + */ + +#include <linux/io.h> +#include <linux/sh_clk.h> +#include <linux/clkdev.h> +#include "clock.h" +#include "common.h" + +#define MSTPCR0 IOMEM(0xffc80030) +#define MSTPCR1 IOMEM(0xffc80034) +#define MSTPCR3 IOMEM(0xffc8003c) +#define MSTPSR1 IOMEM(0xffc80044) +#define MSTPSR4 IOMEM(0xffc80048) +#define MSTPSR6 IOMEM(0xffc8004c) +#define MSTPCR4 IOMEM(0xffc80050) +#define MSTPCR5 IOMEM(0xffc80054) +#define MSTPCR6 IOMEM(0xffc80058) +#define MODEMR 0xFFCC0020 + +#define MD(nr) BIT(nr) + +/* ioremap() through clock mapping mandatory to avoid + * collision with ARM coherent DMA virtual memory range. + */ + +static struct clk_mapping cpg_mapping = { + .phys = 0xffc80000, + .len = 0x80, +}; + +static struct clk extal_clk = { + /* .rate will be updated on r8a7778_clock_init() */ + .mapping = &cpg_mapping, +}; + +static struct clk audio_clk_a = { +}; + +static struct clk audio_clk_b = { +}; + +static struct clk audio_clk_c = { +}; + +/* + * clock ratio of these clock will be updated + * on r8a7778_clock_init() + */ +SH_FIXED_RATIO_CLK_SET(plla_clk, extal_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(pllb_clk, extal_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(i_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(s_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(s1_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(s3_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(s4_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(b_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(out_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(p_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(g_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(z_clk, pllb_clk, 1, 1); + +static struct clk *main_clks[] = { + &extal_clk, + &plla_clk, + &pllb_clk, + &i_clk, + &s_clk, + &s1_clk, + &s3_clk, + &s4_clk, + &b_clk, + &out_clk, + &p_clk, + &g_clk, + &z_clk, + &audio_clk_a, + &audio_clk_b, + &audio_clk_c, +}; + +enum { + MSTP531, MSTP530, + MSTP529, MSTP528, MSTP527, MSTP526, MSTP525, MSTP524, MSTP523, + MSTP331, + MSTP323, MSTP322, MSTP321, + MSTP311, MSTP310, + MSTP309, MSTP308, MSTP307, + MSTP114, + MSTP110, MSTP109, + MSTP100, + MSTP030, + MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021, + MSTP016, MSTP015, MSTP012, MSTP011, MSTP010, + MSTP009, MSTP008, MSTP007, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP531] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 31, 0), /* SCU0 */ + [MSTP530] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 30, 0), /* SCU1 */ + [MSTP529] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 29, 0), /* SCU2 */ + [MSTP528] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 28, 0), /* SCU3 */ + [MSTP527] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 27, 0), /* SCU4 */ + [MSTP526] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 26, 0), /* SCU5 */ + [MSTP525] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 25, 0), /* SCU6 */ + [MSTP524] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 24, 0), /* SCU7 */ + [MSTP523] = SH_CLK_MSTP32(&p_clk, MSTPCR5, 23, 0), /* SCU8 */ + [MSTP331] = SH_CLK_MSTP32(&s4_clk, MSTPCR3, 31, 0), /* MMC */ + [MSTP323] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 23, 0), /* SDHI0 */ + [MSTP322] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 22, 0), /* SDHI1 */ + [MSTP321] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 21, 0), /* SDHI2 */ + [MSTP311] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 11, 0), /* SSI4 */ + [MSTP310] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 10, 0), /* SSI5 */ + [MSTP309] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 9, 0), /* SSI6 */ + [MSTP308] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 8, 0), /* SSI7 */ + [MSTP307] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 7, 0), /* SSI8 */ + [MSTP114] = SH_CLK_MSTP32(&p_clk, MSTPCR1, 14, 0), /* Ether */ + [MSTP110] = SH_CLK_MSTP32(&s_clk, MSTPCR1, 10, 0), /* VIN0 */ + [MSTP109] = SH_CLK_MSTP32(&s_clk, MSTPCR1, 9, 0), /* VIN1 */ + [MSTP100] = SH_CLK_MSTP32(&p_clk, MSTPCR1, 0, 0), /* USB0/1 */ + [MSTP030] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 30, 0), /* I2C0 */ + [MSTP029] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 29, 0), /* I2C1 */ + [MSTP028] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 28, 0), /* I2C2 */ + [MSTP027] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 27, 0), /* I2C3 */ + [MSTP026] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 26, 0), /* SCIF0 */ + [MSTP025] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 25, 0), /* SCIF1 */ + [MSTP024] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 24, 0), /* SCIF2 */ + [MSTP023] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 23, 0), /* SCIF3 */ + [MSTP022] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 22, 0), /* SCIF4 */ + [MSTP021] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 21, 0), /* SCIF5 */ + [MSTP016] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 16, 0), /* TMU0 */ + [MSTP015] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 15, 0), /* TMU1 */ + [MSTP012] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 12, 0), /* SSI0 */ + [MSTP011] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 11, 0), /* SSI1 */ + [MSTP010] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 10, 0), /* SSI2 */ + [MSTP009] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 9, 0), /* SSI3 */ + [MSTP008] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 8, 0), /* SRU */ + [MSTP007] = SH_CLK_MSTP32(&s_clk, MSTPCR0, 7, 0), /* HSPI */ +}; + +static struct clk_lookup lookups[] = { + /* main */ + CLKDEV_CON_ID("shyway_clk", &s_clk), + CLKDEV_CON_ID("peripheral_clk", &p_clk), + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("sh_mmcif", &mstp_clks[MSTP331]), /* MMC */ + CLKDEV_DEV_ID("ffe4e000.mmc", &mstp_clks[MSTP331]), /* MMC */ + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP323]), /* SDHI0 */ + CLKDEV_DEV_ID("ffe4c000.sd", &mstp_clks[MSTP323]), /* SDHI0 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP322]), /* SDHI1 */ + CLKDEV_DEV_ID("ffe4d000.sd", &mstp_clks[MSTP322]), /* SDHI1 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP321]), /* SDHI2 */ + CLKDEV_DEV_ID("ffe4f000.sd", &mstp_clks[MSTP321]), /* SDHI2 */ + CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */ + CLKDEV_DEV_ID("r8a7778-vin.0", &mstp_clks[MSTP110]), /* VIN0 */ + CLKDEV_DEV_ID("r8a7778-vin.1", &mstp_clks[MSTP109]), /* VIN1 */ + CLKDEV_DEV_ID("ehci-platform", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */ + CLKDEV_DEV_ID("ohci-platform", &mstp_clks[MSTP100]), /* USB OHCI port0/1 */ + CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP100]), /* USB FUNC */ + CLKDEV_DEV_ID("i2c-rcar.0", &mstp_clks[MSTP030]), /* I2C0 */ + CLKDEV_DEV_ID("ffc70000.i2c", &mstp_clks[MSTP030]), /* I2C0 */ + CLKDEV_DEV_ID("i2c-rcar.1", &mstp_clks[MSTP029]), /* I2C1 */ + CLKDEV_DEV_ID("ffc71000.i2c", &mstp_clks[MSTP029]), /* I2C1 */ + CLKDEV_DEV_ID("i2c-rcar.2", &mstp_clks[MSTP028]), /* I2C2 */ + CLKDEV_DEV_ID("ffc72000.i2c", &mstp_clks[MSTP028]), /* I2C2 */ + CLKDEV_DEV_ID("i2c-rcar.3", &mstp_clks[MSTP027]), /* I2C3 */ + CLKDEV_DEV_ID("ffc73000.i2c", &mstp_clks[MSTP027]), /* I2C3 */ + CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP026]), /* SCIF0 */ + CLKDEV_DEV_ID("ffe40000.serial", &mstp_clks[MSTP026]), /* SCIF0 */ + CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP025]), /* SCIF1 */ + CLKDEV_DEV_ID("ffe41000.serial", &mstp_clks[MSTP025]), /* SCIF1 */ + CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP024]), /* SCIF2 */ + CLKDEV_DEV_ID("ffe42000.serial", &mstp_clks[MSTP024]), /* SCIF2 */ + CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP023]), /* SCIF3 */ + CLKDEV_DEV_ID("ffe43000.serial", &mstp_clks[MSTP023]), /* SCIF3 */ + CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP022]), /* SCIF4 */ + CLKDEV_DEV_ID("ffe44000.serial", &mstp_clks[MSTP022]), /* SCIF4 */ + CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP021]), /* SCIF6 */ + CLKDEV_DEV_ID("ffe45000.serial", &mstp_clks[MSTP021]), /* SCIF5 */ + CLKDEV_DEV_ID("sh-hspi.0", &mstp_clks[MSTP007]), /* HSPI0 */ + CLKDEV_DEV_ID("fffc7000.spi", &mstp_clks[MSTP007]), /* HSPI0 */ + CLKDEV_DEV_ID("sh-hspi.1", &mstp_clks[MSTP007]), /* HSPI1 */ + CLKDEV_DEV_ID("fffc8000.spi", &mstp_clks[MSTP007]), /* HSPI1 */ + CLKDEV_DEV_ID("sh-hspi.2", &mstp_clks[MSTP007]), /* HSPI2 */ + CLKDEV_DEV_ID("fffc6000.spi", &mstp_clks[MSTP007]), /* HSPI2 */ + CLKDEV_DEV_ID("rcar_sound", &mstp_clks[MSTP008]), /* SRU */ + + CLKDEV_ICK_ID("clk_a", "rcar_sound", &audio_clk_a), + CLKDEV_ICK_ID("clk_b", "rcar_sound", &audio_clk_b), + CLKDEV_ICK_ID("clk_c", "rcar_sound", &audio_clk_c), + CLKDEV_ICK_ID("clk_i", "rcar_sound", &s1_clk), + CLKDEV_ICK_ID("ssi.0", "rcar_sound", &mstp_clks[MSTP012]), + CLKDEV_ICK_ID("ssi.1", "rcar_sound", &mstp_clks[MSTP011]), + CLKDEV_ICK_ID("ssi.2", "rcar_sound", &mstp_clks[MSTP010]), + CLKDEV_ICK_ID("ssi.3", "rcar_sound", &mstp_clks[MSTP009]), + CLKDEV_ICK_ID("ssi.4", "rcar_sound", &mstp_clks[MSTP311]), + CLKDEV_ICK_ID("ssi.5", "rcar_sound", &mstp_clks[MSTP310]), + CLKDEV_ICK_ID("ssi.6", "rcar_sound", &mstp_clks[MSTP309]), + CLKDEV_ICK_ID("ssi.7", "rcar_sound", &mstp_clks[MSTP308]), + CLKDEV_ICK_ID("ssi.8", "rcar_sound", &mstp_clks[MSTP307]), + CLKDEV_ICK_ID("src.0", "rcar_sound", &mstp_clks[MSTP531]), + CLKDEV_ICK_ID("src.1", "rcar_sound", &mstp_clks[MSTP530]), + CLKDEV_ICK_ID("src.2", "rcar_sound", &mstp_clks[MSTP529]), + CLKDEV_ICK_ID("src.3", "rcar_sound", &mstp_clks[MSTP528]), + CLKDEV_ICK_ID("src.4", "rcar_sound", &mstp_clks[MSTP527]), + CLKDEV_ICK_ID("src.5", "rcar_sound", &mstp_clks[MSTP526]), + CLKDEV_ICK_ID("src.6", "rcar_sound", &mstp_clks[MSTP525]), + CLKDEV_ICK_ID("src.7", "rcar_sound", &mstp_clks[MSTP524]), + CLKDEV_ICK_ID("src.8", "rcar_sound", &mstp_clks[MSTP523]), + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP016]), + CLKDEV_ICK_ID("fck", "ffd80000.timer", &mstp_clks[MSTP016]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP015]), + CLKDEV_ICK_ID("fck", "ffd81000.timer", &mstp_clks[MSTP015]), +}; + +void __init r8a7778_clock_init(void) +{ + void __iomem *modemr = ioremap_nocache(MODEMR, PAGE_SIZE); + u32 mode; + int k, ret = 0; + + BUG_ON(!modemr); + mode = ioread32(modemr); + iounmap(modemr); + + switch (mode & (MD(19) | MD(18) | MD(12) | MD(11))) { + case MD(19): + extal_clk.rate = 38000000; + SH_CLK_SET_RATIO(&plla_clk_ratio, 21, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 21, 1); + break; + case MD(19) | MD(11): + extal_clk.rate = 33333333; + SH_CLK_SET_RATIO(&plla_clk_ratio, 24, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 24, 1); + break; + case MD(19) | MD(12): + extal_clk.rate = 28500000; + SH_CLK_SET_RATIO(&plla_clk_ratio, 28, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 28, 1); + break; + case MD(19) | MD(12) | MD(11): + extal_clk.rate = 25000000; + SH_CLK_SET_RATIO(&plla_clk_ratio, 32, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 32, 1); + break; + case MD(19) | MD(18) | MD(11): + extal_clk.rate = 33333333; + SH_CLK_SET_RATIO(&plla_clk_ratio, 24, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 21, 1); + break; + case MD(19) | MD(18) | MD(12): + extal_clk.rate = 28500000; + SH_CLK_SET_RATIO(&plla_clk_ratio, 28, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 21, 1); + break; + case MD(19) | MD(18) | MD(12) | MD(11): + extal_clk.rate = 25000000; + SH_CLK_SET_RATIO(&plla_clk_ratio, 32, 1); + SH_CLK_SET_RATIO(&pllb_clk_ratio, 24, 1); + break; + default: + BUG(); + } + + if (mode & MD(1)) { + SH_CLK_SET_RATIO(&i_clk_ratio, 1, 1); + SH_CLK_SET_RATIO(&s_clk_ratio, 1, 3); + SH_CLK_SET_RATIO(&s1_clk_ratio, 1, 6); + SH_CLK_SET_RATIO(&s3_clk_ratio, 1, 4); + SH_CLK_SET_RATIO(&s4_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&p_clk_ratio, 1, 12); + SH_CLK_SET_RATIO(&g_clk_ratio, 1, 12); + if (mode & MD(2)) { + SH_CLK_SET_RATIO(&b_clk_ratio, 1, 18); + SH_CLK_SET_RATIO(&out_clk_ratio, 1, 18); + } else { + SH_CLK_SET_RATIO(&b_clk_ratio, 1, 12); + SH_CLK_SET_RATIO(&out_clk_ratio, 1, 12); + } + } else { + SH_CLK_SET_RATIO(&i_clk_ratio, 1, 1); + SH_CLK_SET_RATIO(&s_clk_ratio, 1, 4); + SH_CLK_SET_RATIO(&s1_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&s3_clk_ratio, 1, 4); + SH_CLK_SET_RATIO(&s4_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&p_clk_ratio, 1, 16); + SH_CLK_SET_RATIO(&g_clk_ratio, 1, 12); + if (mode & MD(2)) { + SH_CLK_SET_RATIO(&b_clk_ratio, 1, 16); + SH_CLK_SET_RATIO(&out_clk_ratio, 1, 16); + } else { + SH_CLK_SET_RATIO(&b_clk_ratio, 1, 12); + SH_CLK_SET_RATIO(&out_clk_ratio, 1, 12); + } + } + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + shmobile_clk_init(); + else + panic("failed to setup r8a7778 clocks\n"); +} diff --git a/arch/arm/mach-shmobile/clock-r8a7779.c b/arch/arm/mach-shmobile/clock-r8a7779.c new file mode 100644 index 000000000..fa8ab2cc9 --- /dev/null +++ b/arch/arm/mach-shmobile/clock-r8a7779.c @@ -0,0 +1,271 @@ +/* + * r8a7779 clock framework support + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 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; either 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. + */ +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/sh_clk.h> +#include <linux/clkdev.h> +#include <linux/sh_timer.h> + +#include "clock.h" +#include "common.h" +#include "r8a7779.h" + +/* + * MD1 = 1 MD1 = 0 + * (PLLA = 1500) (PLLA = 1600) + * (MHz) (MHz) + *------------------------------------------------+-------------------- + * clkz 1000 (2/3) 800 (1/2) + * clkzs 250 (1/6) 200 (1/8) + * clki 750 (1/2) 800 (1/2) + * clks 250 (1/6) 200 (1/8) + * clks1 125 (1/12) 100 (1/16) + * clks3 187.5 (1/8) 200 (1/8) + * clks4 93.7 (1/16) 100 (1/16) + * clkp 62.5 (1/24) 50 (1/32) + * clkg 62.5 (1/24) 66.6 (1/24) + * clkb, CLKOUT + * (MD2 = 0) 62.5 (1/24) 66.6 (1/24) + * (MD2 = 1) 41.6 (1/36) 50 (1/32) +*/ + +#define MD(nr) BIT(nr) + +#define MSTPCR0 IOMEM(0xffc80030) +#define MSTPCR1 IOMEM(0xffc80034) +#define MSTPCR3 IOMEM(0xffc8003c) +#define MSTPSR1 IOMEM(0xffc80044) + +/* ioremap() through clock mapping mandatory to avoid + * collision with ARM coherent DMA virtual memory range. + */ + +static struct clk_mapping cpg_mapping = { + .phys = 0xffc80000, + .len = 0x80, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk plla_clk = { + /* .rate will be updated on r8a7779_clock_init() */ + .mapping = &cpg_mapping, +}; + +/* + * clock ratio of these clock will be updated + * on r8a7779_clock_init() + */ +SH_FIXED_RATIO_CLK_SET(clkz_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clkzs_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clki_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clks_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clks1_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clks3_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clks4_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clkb_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clkout_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clkp_clk, plla_clk, 1, 1); +SH_FIXED_RATIO_CLK_SET(clkg_clk, plla_clk, 1, 1); + +static struct clk *main_clks[] = { + &plla_clk, + &clkz_clk, + &clkzs_clk, + &clki_clk, + &clks_clk, + &clks1_clk, + &clks3_clk, + &clks4_clk, + &clkb_clk, + &clkout_clk, + &clkp_clk, + &clkg_clk, +}; + +enum { MSTP323, MSTP322, MSTP321, MSTP320, + MSTP120, + MSTP116, MSTP115, MSTP114, + MSTP110, MSTP109, MSTP108, + MSTP103, MSTP101, MSTP100, + MSTP030, + MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021, + MSTP016, MSTP015, MSTP014, + MSTP007, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP323] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 23, 0), /* SDHI0 */ + [MSTP322] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 22, 0), /* SDHI1 */ + [MSTP321] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 21, 0), /* SDHI2 */ + [MSTP320] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 20, 0), /* SDHI3 */ + [MSTP120] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 20, MSTPSR1, 0), /* VIN3 */ + [MSTP116] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 16, MSTPSR1, 0), /* PCIe */ + [MSTP115] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 15, MSTPSR1, 0), /* SATA */ + [MSTP114] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 14, MSTPSR1, 0), /* Ether */ + [MSTP110] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 10, MSTPSR1, 0), /* VIN0 */ + [MSTP109] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 9, MSTPSR1, 0), /* VIN1 */ + [MSTP108] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 8, MSTPSR1, 0), /* VIN2 */ + [MSTP103] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 3, MSTPSR1, 0), /* DU */ + [MSTP101] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 1, MSTPSR1, 0), /* USB2 */ + [MSTP100] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 0, MSTPSR1, 0), /* USB0/1 */ + [MSTP030] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 30, 0), /* I2C0 */ + [MSTP029] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 29, 0), /* I2C1 */ + [MSTP028] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 28, 0), /* I2C2 */ + [MSTP027] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 27, 0), /* I2C3 */ + [MSTP026] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 26, 0), /* SCIF0 */ + [MSTP025] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 25, 0), /* SCIF1 */ + [MSTP024] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 24, 0), /* SCIF2 */ + [MSTP023] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 23, 0), /* SCIF3 */ + [MSTP022] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 22, 0), /* SCIF4 */ + [MSTP021] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 21, 0), /* SCIF5 */ + [MSTP016] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 16, 0), /* TMU0 */ + [MSTP015] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 15, 0), /* TMU1 */ + [MSTP014] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 14, 0), /* TMU2 */ + [MSTP007] = SH_CLK_MSTP32(&clks_clk, MSTPCR0, 7, 0), /* HSPI */ +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("plla_clk", &plla_clk), + CLKDEV_CON_ID("clkz_clk", &clkz_clk), + CLKDEV_CON_ID("clkzs_clk", &clkzs_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("shyway_clk", &clks_clk), + CLKDEV_CON_ID("bus_clk", &clkout_clk), + CLKDEV_CON_ID("shyway4_clk", &clks4_clk), + CLKDEV_CON_ID("shyway3_clk", &clks3_clk), + CLKDEV_CON_ID("shyway1_clk", &clks1_clk), + CLKDEV_CON_ID("peripheral_clk", &clkp_clk), + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("r8a7779-vin.3", &mstp_clks[MSTP120]), /* VIN3 */ + CLKDEV_DEV_ID("rcar-pcie", &mstp_clks[MSTP116]), /* PCIe */ + CLKDEV_DEV_ID("sata_rcar", &mstp_clks[MSTP115]), /* SATA */ + CLKDEV_DEV_ID("fc600000.sata", &mstp_clks[MSTP115]), /* SATA w/DT */ + CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */ + CLKDEV_DEV_ID("r8a7779-vin.0", &mstp_clks[MSTP110]), /* VIN0 */ + CLKDEV_DEV_ID("r8a7779-vin.1", &mstp_clks[MSTP109]), /* VIN1 */ + CLKDEV_DEV_ID("r8a7779-vin.2", &mstp_clks[MSTP108]), /* VIN2 */ + CLKDEV_DEV_ID("ehci-platform.1", &mstp_clks[MSTP101]), /* USB EHCI port2 */ + CLKDEV_DEV_ID("ohci-platform.1", &mstp_clks[MSTP101]), /* USB OHCI port2 */ + CLKDEV_DEV_ID("ehci-platform.0", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */ + CLKDEV_DEV_ID("ohci-platform.0", &mstp_clks[MSTP100]), /* USB OHCI port0/1 */ + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP016]), /* TMU0 */ + CLKDEV_DEV_ID("i2c-rcar.0", &mstp_clks[MSTP030]), /* I2C0 */ + CLKDEV_DEV_ID("ffc70000.i2c", &mstp_clks[MSTP030]), /* I2C0 */ + CLKDEV_DEV_ID("i2c-rcar.1", &mstp_clks[MSTP029]), /* I2C1 */ + CLKDEV_DEV_ID("ffc71000.i2c", &mstp_clks[MSTP029]), /* I2C1 */ + CLKDEV_DEV_ID("i2c-rcar.2", &mstp_clks[MSTP028]), /* I2C2 */ + CLKDEV_DEV_ID("ffc72000.i2c", &mstp_clks[MSTP028]), /* I2C2 */ + CLKDEV_DEV_ID("i2c-rcar.3", &mstp_clks[MSTP027]), /* I2C3 */ + CLKDEV_DEV_ID("ffc73000.i2c", &mstp_clks[MSTP027]), /* I2C3 */ + CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP026]), /* SCIF0 */ + CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP025]), /* SCIF1 */ + CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP024]), /* SCIF2 */ + CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP023]), /* SCIF3 */ + CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP022]), /* SCIF4 */ + CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP021]), /* SCIF6 */ + CLKDEV_DEV_ID("sh-hspi.0", &mstp_clks[MSTP007]), /* HSPI0 */ + CLKDEV_DEV_ID("fffc7000.spi", &mstp_clks[MSTP007]), /* HSPI0 */ + CLKDEV_DEV_ID("sh-hspi.1", &mstp_clks[MSTP007]), /* HSPI1 */ + CLKDEV_DEV_ID("fffc8000.spi", &mstp_clks[MSTP007]), /* HSPI1 */ + CLKDEV_DEV_ID("sh-hspi.2", &mstp_clks[MSTP007]), /* HSPI2 */ + CLKDEV_DEV_ID("fffc6000.spi", &mstp_clks[MSTP007]), /* HSPI2 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP323]), /* SDHI0 */ + CLKDEV_DEV_ID("ffe4c000.sd", &mstp_clks[MSTP323]), /* SDHI0 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP322]), /* SDHI1 */ + CLKDEV_DEV_ID("ffe4d000.sd", &mstp_clks[MSTP322]), /* SDHI1 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP321]), /* SDHI2 */ + CLKDEV_DEV_ID("ffe4e000.sd", &mstp_clks[MSTP321]), /* SDHI2 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.3", &mstp_clks[MSTP320]), /* SDHI3 */ + CLKDEV_DEV_ID("ffe4f000.sd", &mstp_clks[MSTP320]), /* SDHI3 */ + CLKDEV_DEV_ID("rcar-du-r8a7779", &mstp_clks[MSTP103]), /* DU */ +}; + +void __init r8a7779_clock_init(void) +{ + u32 mode = r8a7779_read_mode_pins(); + int k, ret = 0; + + if (mode & MD(1)) { + plla_clk.rate = 1500000000; + + SH_CLK_SET_RATIO(&clkz_clk_ratio, 2, 3); + SH_CLK_SET_RATIO(&clkzs_clk_ratio, 1, 6); + SH_CLK_SET_RATIO(&clki_clk_ratio, 1, 2); + SH_CLK_SET_RATIO(&clks_clk_ratio, 1, 6); + SH_CLK_SET_RATIO(&clks1_clk_ratio, 1, 12); + SH_CLK_SET_RATIO(&clks3_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&clks4_clk_ratio, 1, 16); + SH_CLK_SET_RATIO(&clkp_clk_ratio, 1, 24); + SH_CLK_SET_RATIO(&clkg_clk_ratio, 1, 24); + if (mode & MD(2)) { + SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 36); + SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 36); + } else { + SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 24); + SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 24); + } + } else { + plla_clk.rate = 1600000000; + + SH_CLK_SET_RATIO(&clkz_clk_ratio, 1, 2); + SH_CLK_SET_RATIO(&clkzs_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&clki_clk_ratio, 1, 2); + SH_CLK_SET_RATIO(&clks_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&clks1_clk_ratio, 1, 16); + SH_CLK_SET_RATIO(&clks3_clk_ratio, 1, 8); + SH_CLK_SET_RATIO(&clks4_clk_ratio, 1, 16); + SH_CLK_SET_RATIO(&clkp_clk_ratio, 1, 32); + SH_CLK_SET_RATIO(&clkg_clk_ratio, 1, 24); + if (mode & MD(2)) { + SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 32); + SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 32); + } else { + SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 24); + SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 24); + } + } + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + shmobile_clk_init(); + else + panic("failed to setup r8a7779 clocks\n"); +} + +/* do nothing for !CONFIG_SMP or !CONFIG_HAVE_TWD */ +void __init __weak r8a7779_register_twd(void) { } + +void __init r8a7779_earlytimer_init(void) +{ + r8a7779_clock_init(); + r8a7779_register_twd(); + shmobile_earlytimer_init(); +} diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c new file mode 100644 index 000000000..3855fb024 --- /dev/null +++ b/arch/arm/mach-shmobile/clock-sh73a0.c @@ -0,0 +1,752 @@ +/* + * sh73a0 clock framework support + * + * Copyright (C) 2010 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; either 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. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/sh_clk.h> +#include <linux/clkdev.h> +#include <asm/processor.h> +#include "clock.h" +#include "common.h" + +#define FRQCRA IOMEM(0xe6150000) +#define FRQCRB IOMEM(0xe6150004) +#define FRQCRD IOMEM(0xe61500e4) +#define VCLKCR1 IOMEM(0xe6150008) +#define VCLKCR2 IOMEM(0xe615000C) +#define VCLKCR3 IOMEM(0xe615001C) +#define ZBCKCR IOMEM(0xe6150010) +#define FLCKCR IOMEM(0xe6150014) +#define SD0CKCR IOMEM(0xe6150074) +#define SD1CKCR IOMEM(0xe6150078) +#define SD2CKCR IOMEM(0xe615007C) +#define FSIACKCR IOMEM(0xe6150018) +#define FSIBCKCR IOMEM(0xe6150090) +#define SUBCKCR IOMEM(0xe6150080) +#define SPUACKCR IOMEM(0xe6150084) +#define SPUVCKCR IOMEM(0xe6150094) +#define MSUCKCR IOMEM(0xe6150088) +#define HSICKCR IOMEM(0xe615008C) +#define MFCK1CR IOMEM(0xe6150098) +#define MFCK2CR IOMEM(0xe615009C) +#define DSITCKCR IOMEM(0xe6150060) +#define DSI0PCKCR IOMEM(0xe6150064) +#define DSI1PCKCR IOMEM(0xe6150068) +#define DSI0PHYCR 0xe615006C +#define DSI1PHYCR 0xe6150070 +#define PLLECR IOMEM(0xe61500d0) +#define PLL0CR IOMEM(0xe61500d8) +#define PLL1CR IOMEM(0xe6150028) +#define PLL2CR IOMEM(0xe615002c) +#define PLL3CR IOMEM(0xe61500dc) +#define SMSTPCR0 IOMEM(0xe6150130) +#define SMSTPCR1 IOMEM(0xe6150134) +#define SMSTPCR2 IOMEM(0xe6150138) +#define SMSTPCR3 IOMEM(0xe615013c) +#define SMSTPCR4 IOMEM(0xe6150140) +#define SMSTPCR5 IOMEM(0xe6150144) +#define CKSCR IOMEM(0xe61500c0) + +/* Fixed 32 KHz root clock from EXTALR pin */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * 26MHz default rate for the EXTAL1 root input clock. + * If needed, reset this with clk_set_rate() from the platform code. + */ +struct clk sh73a0_extal1_clk = { + .rate = 26000000, +}; + +/* + * 48MHz default rate for the EXTAL2 root input clock. + * If needed, reset this with clk_set_rate() from the platform code. + */ +struct clk sh73a0_extal2_clk = { + .rate = 48000000, +}; + +static struct sh_clk_ops main_clk_ops = { + .recalc = followparent_recalc, +}; + +/* Main clock */ +static struct clk main_clk = { + /* .parent wll be set on sh73a0_clock_init() */ + .ops = &main_clk_ops, +}; + +/* PLL0, PLL1, PLL2, PLL3 */ +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long mult = 1; + + if (__raw_readl(PLLECR) & (1 << clk->enable_bit)) { + mult = (((__raw_readl(clk->enable_reg) >> 24) & 0x3f) + 1); + /* handle CFG bit for PLL1 and PLL2 */ + switch (clk->enable_bit) { + case 1: + case 2: + if (__raw_readl(clk->enable_reg) & (1 << 20)) + mult *= 2; + } + } + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll0_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, + .parent = &main_clk, + .enable_reg = (void __iomem *)PLL0CR, + .enable_bit = 0, +}; + +static struct clk pll1_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, + .parent = &main_clk, + .enable_reg = (void __iomem *)PLL1CR, + .enable_bit = 1, +}; + +static struct clk pll2_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, + .parent = &main_clk, + .enable_reg = (void __iomem *)PLL2CR, + .enable_bit = 2, +}; + +static struct clk pll3_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, + .parent = &main_clk, + .enable_reg = (void __iomem *)PLL3CR, + .enable_bit = 3, +}; + +/* A fixed divide block */ +SH_CLK_RATIO(div2, 1, 2); +SH_CLK_RATIO(div7, 1, 7); +SH_CLK_RATIO(div13, 1, 13); + +SH_FIXED_RATIO_CLK(extal1_div2_clk, sh73a0_extal1_clk, div2); +SH_FIXED_RATIO_CLK(extal2_div2_clk, sh73a0_extal2_clk, div2); +SH_FIXED_RATIO_CLK(main_div2_clk, main_clk, div2); +SH_FIXED_RATIO_CLK(pll1_div2_clk, pll1_clk, div2); +SH_FIXED_RATIO_CLK(pll1_div7_clk, pll1_clk, div7); +SH_FIXED_RATIO_CLK(pll1_div13_clk, pll1_clk, div13); + +/* External input clock */ +struct clk sh73a0_extcki_clk = { +}; + +struct clk sh73a0_extalr_clk = { +}; + +static struct clk *main_clks[] = { + &r_clk, + &sh73a0_extal1_clk, + &sh73a0_extal2_clk, + &extal1_div2_clk, + &extal2_div2_clk, + &main_clk, + &main_div2_clk, + &pll0_clk, + &pll1_clk, + &pll2_clk, + &pll3_clk, + &pll1_div2_clk, + &pll1_div7_clk, + &pll1_div13_clk, + &sh73a0_extcki_clk, + &sh73a0_extalr_clk, +}; + +static int frqcr_kick(void) +{ + int i; + + /* set KICK bit in FRQCRB to update hardware setting, check success */ + __raw_writel(__raw_readl(FRQCRB) | (1 << 31), FRQCRB); + for (i = 1000; i; i--) + if (__raw_readl(FRQCRB) & (1 << 31)) + cpu_relax(); + else + return i; + + return -ETIMEDOUT; +} + +static void div4_kick(struct clk *clk) +{ + frqcr_kick(); +} + +static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, + 24, 0, 36, 48, 7 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, + .kick = div4_kick, +}; + +enum { DIV4_I, DIV4_ZG, DIV4_M3, DIV4_B, DIV4_M1, DIV4_M2, + DIV4_Z, DIV4_ZX, DIV4_HP, DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll1_clk, _reg, _bit, _mask, _flags) + +static struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCRA, 20, 0xdff, CLK_ENABLE_ON_INIT), + /* + * ZG clock is dividing PLL0 frequency to supply SGX. Make sure not to + * exceed maximum frequencies of 201.5MHz for VDD_DVFS=1.175 and + * 239.2MHz for VDD_DVFS=1.315V. + */ + [DIV4_ZG] = SH_CLK_DIV4(&pll0_clk, FRQCRA, 16, 0xd7f, CLK_ENABLE_ON_INIT), + [DIV4_M3] = DIV4(FRQCRA, 12, 0x1dff, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCRA, 8, 0xdff, CLK_ENABLE_ON_INIT), + [DIV4_M1] = DIV4(FRQCRA, 4, 0x1dff, 0), + [DIV4_M2] = DIV4(FRQCRA, 0, 0x1dff, 0), + [DIV4_Z] = SH_CLK_DIV4(&pll0_clk, FRQCRB, 24, 0x97f, 0), + [DIV4_ZX] = DIV4(FRQCRB, 12, 0xdff, 0), + [DIV4_HP] = DIV4(FRQCRB, 4, 0xdff, 0), +}; + +static unsigned long twd_recalc(struct clk *clk) +{ + return clk_get_rate(clk->parent) / 4; +} + +static struct sh_clk_ops twd_clk_ops = { + .recalc = twd_recalc, +}; + +static struct clk twd_clk = { + .parent = &div4_clks[DIV4_Z], + .ops = &twd_clk_ops, +}; + +static struct sh_clk_ops zclk_ops, kicker_ops; +static const struct sh_clk_ops *div4_clk_ops; + +static int zclk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + + if (!clk->parent || !__clk_get(clk->parent)) + return -ENODEV; + + if (readl(FRQCRB) & (1 << 31)) + return -EBUSY; + + if (rate == clk_get_rate(clk->parent)) { + /* 1:1 - switch off divider */ + __raw_writel(__raw_readl(FRQCRB) & ~(1 << 28), FRQCRB); + /* nullify the divider to prepare for the next time */ + ret = div4_clk_ops->set_rate(clk, rate / 2); + if (!ret) + ret = frqcr_kick(); + if (ret > 0) + ret = 0; + } else { + /* Enable the divider */ + __raw_writel(__raw_readl(FRQCRB) | (1 << 28), FRQCRB); + + ret = frqcr_kick(); + if (ret >= 0) + /* + * set the divider - call the DIV4 method, it will kick + * FRQCRB too + */ + ret = div4_clk_ops->set_rate(clk, rate); + if (ret < 0) + goto esetrate; + } + +esetrate: + __clk_put(clk->parent); + return ret; +} + +static long zclk_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long div_freq = div4_clk_ops->round_rate(clk, rate), + parent_freq = clk_get_rate(clk->parent); + + if (rate > div_freq && abs(parent_freq - rate) < rate - div_freq) + return parent_freq; + + return div_freq; +} + +static unsigned long zclk_recalc(struct clk *clk) +{ + /* + * Must recalculate frequencies in case PLL0 has been changed, even if + * the divisor is unused ATM! + */ + unsigned long div_freq = div4_clk_ops->recalc(clk); + + if (__raw_readl(FRQCRB) & (1 << 28)) + return div_freq; + + return clk_get_rate(clk->parent); +} + +static int kicker_set_rate(struct clk *clk, unsigned long rate) +{ + if (__raw_readl(FRQCRB) & (1 << 31)) + return -EBUSY; + + return div4_clk_ops->set_rate(clk, rate); +} + +static void div4_clk_extend(void) +{ + int i; + + div4_clk_ops = div4_clks[0].ops; + + /* Add a kicker-busy check before changing the rate */ + kicker_ops = *div4_clk_ops; + /* We extend the DIV4 clock with a 1:1 pass-through case */ + zclk_ops = *div4_clk_ops; + + kicker_ops.set_rate = kicker_set_rate; + zclk_ops.set_rate = zclk_set_rate; + zclk_ops.round_rate = zclk_round_rate; + zclk_ops.recalc = zclk_recalc; + + for (i = 0; i < DIV4_NR; i++) + div4_clks[i].ops = i == DIV4_Z ? &zclk_ops : &kicker_ops; +} + +enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_ZB1, + DIV6_FLCTL, DIV6_SDHI0, DIV6_SDHI1, DIV6_SDHI2, + DIV6_FSIA, DIV6_FSIB, DIV6_SUB, + DIV6_SPUA, DIV6_SPUV, DIV6_MSU, + DIV6_HSI, DIV6_MFG1, DIV6_MFG2, + DIV6_DSIT, DIV6_DSI0P, DIV6_DSI1P, + DIV6_NR }; + +static struct clk *vck_parent[8] = { + [0] = &pll1_div2_clk, + [1] = &pll2_clk, + [2] = &sh73a0_extcki_clk, + [3] = &sh73a0_extal2_clk, + [4] = &main_div2_clk, + [5] = &sh73a0_extalr_clk, + [6] = &main_clk, +}; + +static struct clk *pll_parent[4] = { + [0] = &pll1_div2_clk, + [1] = &pll2_clk, + [2] = &pll1_div13_clk, +}; + +static struct clk *hsi_parent[4] = { + [0] = &pll1_div2_clk, + [1] = &pll2_clk, + [2] = &pll1_div7_clk, +}; + +static struct clk *pll_extal2_parent[] = { + [0] = &pll1_div2_clk, + [1] = &pll2_clk, + [2] = &sh73a0_extal2_clk, + [3] = &sh73a0_extal2_clk, +}; + +static struct clk *dsi_parent[8] = { + [0] = &pll1_div2_clk, + [1] = &pll2_clk, + [2] = &main_clk, + [3] = &sh73a0_extal2_clk, + [4] = &sh73a0_extcki_clk, +}; + +static struct clk div6_clks[DIV6_NR] = { + [DIV6_VCK1] = SH_CLK_DIV6_EXT(VCLKCR1, 0, + vck_parent, ARRAY_SIZE(vck_parent), 12, 3), + [DIV6_VCK2] = SH_CLK_DIV6_EXT(VCLKCR2, 0, + vck_parent, ARRAY_SIZE(vck_parent), 12, 3), + [DIV6_VCK3] = SH_CLK_DIV6_EXT(VCLKCR3, 0, + vck_parent, ARRAY_SIZE(vck_parent), 12, 3), + [DIV6_ZB1] = SH_CLK_DIV6_EXT(ZBCKCR, CLK_ENABLE_ON_INIT, + pll_parent, ARRAY_SIZE(pll_parent), 7, 1), + [DIV6_FLCTL] = SH_CLK_DIV6_EXT(FLCKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 7, 1), + [DIV6_SDHI0] = SH_CLK_DIV6_EXT(SD0CKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 6, 2), + [DIV6_SDHI1] = SH_CLK_DIV6_EXT(SD1CKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 6, 2), + [DIV6_SDHI2] = SH_CLK_DIV6_EXT(SD2CKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 6, 2), + [DIV6_FSIA] = SH_CLK_DIV6_EXT(FSIACKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 6, 1), + [DIV6_FSIB] = SH_CLK_DIV6_EXT(FSIBCKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 6, 1), + [DIV6_SUB] = SH_CLK_DIV6_EXT(SUBCKCR, 0, + pll_extal2_parent, ARRAY_SIZE(pll_extal2_parent), 6, 2), + [DIV6_SPUA] = SH_CLK_DIV6_EXT(SPUACKCR, 0, + pll_extal2_parent, ARRAY_SIZE(pll_extal2_parent), 6, 2), + [DIV6_SPUV] = SH_CLK_DIV6_EXT(SPUVCKCR, 0, + pll_extal2_parent, ARRAY_SIZE(pll_extal2_parent), 6, 2), + [DIV6_MSU] = SH_CLK_DIV6_EXT(MSUCKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 7, 1), + [DIV6_HSI] = SH_CLK_DIV6_EXT(HSICKCR, 0, + hsi_parent, ARRAY_SIZE(hsi_parent), 6, 2), + [DIV6_MFG1] = SH_CLK_DIV6_EXT(MFCK1CR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 7, 1), + [DIV6_MFG2] = SH_CLK_DIV6_EXT(MFCK2CR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 7, 1), + [DIV6_DSIT] = SH_CLK_DIV6_EXT(DSITCKCR, 0, + pll_parent, ARRAY_SIZE(pll_parent), 7, 1), + [DIV6_DSI0P] = SH_CLK_DIV6_EXT(DSI0PCKCR, 0, + dsi_parent, ARRAY_SIZE(dsi_parent), 12, 3), + [DIV6_DSI1P] = SH_CLK_DIV6_EXT(DSI1PCKCR, 0, + dsi_parent, ARRAY_SIZE(dsi_parent), 12, 3), +}; + +/* DSI DIV */ +static unsigned long dsiphy_recalc(struct clk *clk) +{ + u32 value; + + value = __raw_readl(clk->mapping->base); + + /* FIXME */ + if (!(value & 0x000B8000)) + return clk->parent->rate; + + value &= 0x3f; + value += 1; + + if ((value < 12) || + (value > 33)) { + pr_err("DSIPHY has wrong value (%d)", value); + return 0; + } + + return clk->parent->rate / value; +} + +static long dsiphy_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_mult_range_round(clk, 12, 33, rate); +} + +static void dsiphy_disable(struct clk *clk) +{ + u32 value; + + value = __raw_readl(clk->mapping->base); + value &= ~0x000B8000; + + __raw_writel(value , clk->mapping->base); +} + +static int dsiphy_enable(struct clk *clk) +{ + u32 value; + int multi; + + value = __raw_readl(clk->mapping->base); + multi = (value & 0x3f) + 1; + + if ((multi < 12) || (multi > 33)) + return -EIO; + + __raw_writel(value | 0x000B8000, clk->mapping->base); + + return 0; +} + +static int dsiphy_set_rate(struct clk *clk, unsigned long rate) +{ + u32 value; + int idx; + + idx = rate / clk->parent->rate; + if ((idx < 12) || (idx > 33)) + return -EINVAL; + + idx += -1; + + value = __raw_readl(clk->mapping->base); + value = (value & ~0x3f) + idx; + + __raw_writel(value, clk->mapping->base); + + return 0; +} + +static struct sh_clk_ops dsiphy_clk_ops = { + .recalc = dsiphy_recalc, + .round_rate = dsiphy_round_rate, + .set_rate = dsiphy_set_rate, + .enable = dsiphy_enable, + .disable = dsiphy_disable, +}; + +static struct clk_mapping dsi0phy_clk_mapping = { + .phys = DSI0PHYCR, + .len = 4, +}; + +static struct clk_mapping dsi1phy_clk_mapping = { + .phys = DSI1PHYCR, + .len = 4, +}; + +static struct clk dsi0phy_clk = { + .ops = &dsiphy_clk_ops, + .parent = &div6_clks[DIV6_DSI0P], /* late install */ + .mapping = &dsi0phy_clk_mapping, +}; + +static struct clk dsi1phy_clk = { + .ops = &dsiphy_clk_ops, + .parent = &div6_clks[DIV6_DSI1P], /* late install */ + .mapping = &dsi1phy_clk_mapping, +}; + +static struct clk *late_main_clks[] = { + &dsi0phy_clk, + &dsi1phy_clk, + &twd_clk, +}; + +enum { MSTP001, + MSTP129, MSTP128, MSTP127, MSTP126, MSTP125, MSTP118, MSTP116, MSTP112, MSTP100, + MSTP219, MSTP218, MSTP217, + MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, + MSTP331, MSTP329, MSTP328, MSTP325, MSTP323, MSTP322, + MSTP314, MSTP313, MSTP312, MSTP311, + MSTP304, MSTP303, MSTP302, MSTP301, MSTP300, + MSTP411, MSTP410, MSTP403, + MSTP508, + MSTP_NR }; + +#define MSTP(_parent, _reg, _bit, _flags) \ + SH_CLK_MSTP32(_parent, _reg, _bit, _flags) + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP001] = MSTP(&div4_clks[DIV4_HP], SMSTPCR0, 1, 0), /* IIC2 */ + [MSTP129] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 29, 0), /* CEU1 */ + [MSTP128] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 28, 0), /* CSI2-RX1 */ + [MSTP127] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 27, 0), /* CEU0 */ + [MSTP126] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 26, 0), /* CSI2-RX0 */ + [MSTP125] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */ + [MSTP118] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 18, 0), /* DSITX0 */ + [MSTP116] = MSTP(&div4_clks[DIV4_HP], SMSTPCR1, 16, 0), /* IIC0 */ + [MSTP112] = MSTP(&div4_clks[DIV4_ZG], SMSTPCR1, 12, 0), /* SGX */ + [MSTP100] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */ + [MSTP219] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 19, 0), /* SCIFA7 */ + [MSTP218] = MSTP(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* SY-DMAC */ + [MSTP217] = MSTP(&div4_clks[DIV4_HP], SMSTPCR2, 17, 0), /* MP-DMAC */ + [MSTP207] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 7, 0), /* SCIFA5 */ + [MSTP206] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 6, 0), /* SCIFB */ + [MSTP204] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 4, 0), /* SCIFA0 */ + [MSTP203] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 3, 0), /* SCIFA1 */ + [MSTP202] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 2, 0), /* SCIFA2 */ + [MSTP201] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 1, 0), /* SCIFA3 */ + [MSTP200] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 0, 0), /* SCIFA4 */ + [MSTP331] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 31, 0), /* SCIFA6 */ + [MSTP329] = MSTP(&r_clk, SMSTPCR3, 29, 0), /* CMT10 */ + [MSTP328] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 28, 0), /*FSI*/ + [MSTP325] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 25, 0), /* IrDA */ + [MSTP323] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 23, 0), /* IIC1 */ + [MSTP322] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 22, 0), /* USB */ + [MSTP314] = MSTP(&div6_clks[DIV6_SDHI0], SMSTPCR3, 14, 0), /* SDHI0 */ + [MSTP313] = MSTP(&div6_clks[DIV6_SDHI1], SMSTPCR3, 13, 0), /* SDHI1 */ + [MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMCIF0 */ + [MSTP311] = MSTP(&div6_clks[DIV6_SDHI2], SMSTPCR3, 11, 0), /* SDHI2 */ + [MSTP304] = MSTP(&main_div2_clk, SMSTPCR3, 4, 0), /* TPU0 */ + [MSTP303] = MSTP(&main_div2_clk, SMSTPCR3, 3, 0), /* TPU1 */ + [MSTP302] = MSTP(&main_div2_clk, SMSTPCR3, 2, 0), /* TPU2 */ + [MSTP301] = MSTP(&main_div2_clk, SMSTPCR3, 1, 0), /* TPU3 */ + [MSTP300] = MSTP(&main_div2_clk, SMSTPCR3, 0, 0), /* TPU4 */ + [MSTP411] = MSTP(&div4_clks[DIV4_HP], SMSTPCR4, 11, 0), /* IIC3 */ + [MSTP410] = MSTP(&div4_clks[DIV4_HP], SMSTPCR4, 10, 0), /* IIC4 */ + [MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */ + [MSTP508] = MSTP(&div4_clks[DIV4_HP], SMSTPCR5, 8, 0), /* INTCA0 */ +}; + +/* The lookups structure below includes duplicate entries for some clocks + * with alternate names. + * - The traditional name used when a device is initialised with platform data + * - The name used when a device is initialised using device tree + * The longer-term aim is to remove these duplicates, and indeed the + * lookups table entirely, by describing clocks using device tree. + */ +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("r_clk", &r_clk), + CLKDEV_DEV_ID("smp_twd", &twd_clk), /* smp_twd */ + + /* DIV4 clocks */ + CLKDEV_DEV_ID("cpu0", &div4_clks[DIV4_Z]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]), + CLKDEV_CON_ID("vck2_clk", &div6_clks[DIV6_VCK2]), + CLKDEV_CON_ID("vck3_clk", &div6_clks[DIV6_VCK3]), + CLKDEV_CON_ID("sdhi0_clk", &div6_clks[DIV6_SDHI0]), + CLKDEV_CON_ID("sdhi1_clk", &div6_clks[DIV6_SDHI1]), + CLKDEV_CON_ID("sdhi2_clk", &div6_clks[DIV6_SDHI2]), + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* I2C2 */ + CLKDEV_DEV_ID("e6824000.i2c", &mstp_clks[MSTP001]), /* I2C2 */ + CLKDEV_DEV_ID("sh_mobile_ceu.1", &mstp_clks[MSTP129]), /* CEU1 */ + CLKDEV_DEV_ID("sh-mobile-csi2.1", &mstp_clks[MSTP128]), /* CSI2-RX1 */ + CLKDEV_DEV_ID("sh_mobile_ceu.0", &mstp_clks[MSTP127]), /* CEU0 */ + CLKDEV_DEV_ID("sh-mobile-csi2.0", &mstp_clks[MSTP126]), /* CSI2-RX0 */ + CLKDEV_DEV_ID("sh-mipi-dsi.0", &mstp_clks[MSTP118]), /* DSITX */ + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP116]), /* I2C0 */ + CLKDEV_DEV_ID("e6820000.i2c", &mstp_clks[MSTP116]), /* I2C0 */ + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]), /* LCDC0 */ + CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP219]), /* SCIFA7 */ + CLKDEV_DEV_ID("e6cd0000.serial", &mstp_clks[MSTP219]), /* SCIFA7 */ + CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[MSTP218]), /* SY-DMAC */ + CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[MSTP217]), /* MP-DMAC */ + CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), /* SCIFA5 */ + CLKDEV_DEV_ID("e6cb0000.serial", &mstp_clks[MSTP207]), /* SCIFA5 */ + CLKDEV_DEV_ID("sh-sci.8", &mstp_clks[MSTP206]), /* SCIFB */ + CLKDEV_DEV_ID("e6c3000.serial", &mstp_clks[MSTP206]), /* SCIFB */ + CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */ + CLKDEV_DEV_ID("e6c40000.serial", &mstp_clks[MSTP204]), /* SCIFA0 */ + CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), /* SCIFA1 */ + CLKDEV_DEV_ID("e6c50000.serial", &mstp_clks[MSTP203]), /* SCIFA1 */ + CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP202]), /* SCIFA2 */ + CLKDEV_DEV_ID("e6c60000.serial", &mstp_clks[MSTP202]), /* SCIFA2 */ + CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP201]), /* SCIFA3 */ + CLKDEV_DEV_ID("e6c70000.serial", &mstp_clks[MSTP201]), /* SCIFA3 */ + CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP200]), /* SCIFA4 */ + CLKDEV_DEV_ID("e6c80000.serial", &mstp_clks[MSTP200]), /* SCIFA4 */ + CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP331]), /* SCIFA6 */ + CLKDEV_DEV_ID("e6cc0000.serial", &mstp_clks[MSTP331]), /* SCIFA6 */ + CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]), /* FSI */ + CLKDEV_DEV_ID("ec230000.sound", &mstp_clks[MSTP328]), /* FSI */ + CLKDEV_DEV_ID("sh_irda.0", &mstp_clks[MSTP325]), /* IrDA */ + CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]), /* I2C1 */ + CLKDEV_DEV_ID("e6822000.i2c", &mstp_clks[MSTP323]), /* I2C1 */ + CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP322]), /* USB */ + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */ + CLKDEV_DEV_ID("ee100000.sd", &mstp_clks[MSTP314]), /* SDHI0 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */ + CLKDEV_DEV_ID("ee120000.sd", &mstp_clks[MSTP313]), /* SDHI1 */ + CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMCIF0 */ + CLKDEV_DEV_ID("e6bd0000.mmc", &mstp_clks[MSTP312]), /* MMCIF0 */ + CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP311]), /* SDHI2 */ + CLKDEV_DEV_ID("ee140000.sd", &mstp_clks[MSTP311]), /* SDHI2 */ + CLKDEV_DEV_ID("renesas-tpu-pwm.0", &mstp_clks[MSTP304]), /* TPU0 */ + CLKDEV_DEV_ID("renesas-tpu-pwm.1", &mstp_clks[MSTP303]), /* TPU1 */ + CLKDEV_DEV_ID("renesas-tpu-pwm.2", &mstp_clks[MSTP302]), /* TPU2 */ + CLKDEV_DEV_ID("renesas-tpu-pwm.3", &mstp_clks[MSTP301]), /* TPU3 */ + CLKDEV_DEV_ID("renesas-tpu-pwm.4", &mstp_clks[MSTP300]), /* TPU4 */ + CLKDEV_DEV_ID("i2c-sh_mobile.3", &mstp_clks[MSTP411]), /* I2C3 */ + CLKDEV_DEV_ID("e6826000.i2c", &mstp_clks[MSTP411]), /* I2C3 */ + CLKDEV_DEV_ID("i2c-sh_mobile.4", &mstp_clks[MSTP410]), /* I2C4 */ + CLKDEV_DEV_ID("e6828000.i2c", &mstp_clks[MSTP410]), /* I2C4 */ + CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */ + CLKDEV_DEV_ID("renesas_intc_irqpin.0", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("e6900000.irqpin", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("renesas_intc_irqpin.1", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("e6900004.irqpin", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("renesas_intc_irqpin.2", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("e6900008.irqpin", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("renesas_intc_irqpin.3", &mstp_clks[MSTP508]), /* INTCA0 */ + CLKDEV_DEV_ID("e690000c.irqpin", &mstp_clks[MSTP508]), /* INTCA0 */ + + /* ICK */ + CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSIT]), + CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSIT]), + CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]), + CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]), + CLKDEV_ICK_ID("dsiphy_clk", "sh-mipi-dsi.0", &dsi0phy_clk), + CLKDEV_ICK_ID("dsiphy_clk", "sh-mipi-dsi.1", &dsi1phy_clk), + CLKDEV_ICK_ID("fck", "sh-cmt-48.1", &mstp_clks[MSTP329]), /* CMT1 */ + CLKDEV_ICK_ID("fck", "e6138000.timer", &mstp_clks[MSTP329]), /* CMT1 */ + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP125]), /* TMU0 */ +}; + +void __init sh73a0_clock_init(void) +{ + int k, ret = 0; + + /* Set SDHI clocks to a known state */ + __raw_writel(0x108, SD0CKCR); + __raw_writel(0x108, SD1CKCR); + __raw_writel(0x108, SD2CKCR); + + /* detect main clock parent */ + switch ((__raw_readl(CKSCR) >> 28) & 0x03) { + case 0: + main_clk.parent = &sh73a0_extal1_clk; + break; + case 1: + main_clk.parent = &extal1_div2_clk; + break; + case 2: + main_clk.parent = &sh73a0_extal2_clk; + break; + case 3: + main_clk.parent = &extal2_div2_clk; + break; + } + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + if (!ret) { + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + if (!ret) + div4_clk_extend(); + } + + if (!ret) + ret = sh_clk_div6_reparent_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++) + ret = clk_register(late_main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + shmobile_clk_init(); + else + panic("failed to setup sh73a0 clocks\n"); +} diff --git a/arch/arm/mach-shmobile/clock.c b/arch/arm/mach-shmobile/clock.c new file mode 100644 index 000000000..68c2d06d0 --- /dev/null +++ b/arch/arm/mach-shmobile/clock.c @@ -0,0 +1,47 @@ +/* + * SH-Mobile Clock Framework + * + * Copyright (C) 2010 Magnus Damm + * + * Used together with arch/arm/common/clkdev.c and drivers/sh/clk.c. + * + * 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. + * + */ + +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sh_clk.h> + +#include "clock.h" +#include "common.h" + +unsigned long shmobile_fixed_ratio_clk_recalc(struct clk *clk) +{ + struct clk_ratio *p = clk->priv; + + return clk->parent->rate / p->div * p->mul; +}; + +struct sh_clk_ops shmobile_fixed_ratio_clk_ops = { + .recalc = shmobile_fixed_ratio_clk_recalc, +}; + +int __init shmobile_clk_init(void) +{ + /* Kick the child clocks.. */ + recalculate_root_clocks(); + + /* Enable the necessary init clocks */ + clk_enable_init_clocks(); + + return 0; +} diff --git a/arch/arm/mach-shmobile/clock.h b/arch/arm/mach-shmobile/clock.h new file mode 100644 index 000000000..cf3552ea1 --- /dev/null +++ b/arch/arm/mach-shmobile/clock.h @@ -0,0 +1,42 @@ +#ifndef CLOCK_H +#define CLOCK_H + +/* legacy clock implementation */ + +struct clk; +unsigned long shmobile_fixed_ratio_clk_recalc(struct clk *clk); +extern struct sh_clk_ops shmobile_fixed_ratio_clk_ops; + +/* clock ratio */ +struct clk_ratio { + int mul; + int div; +}; + +#define SH_CLK_RATIO(name, m, d) \ +static struct clk_ratio name ##_ratio = { \ + .mul = m, \ + .div = d, \ +} + +#define SH_FIXED_RATIO_CLKg(name, p, r) \ +struct clk name = { \ + .parent = &p, \ + .ops = &shmobile_fixed_ratio_clk_ops,\ + .priv = &r ## _ratio, \ +} + +#define SH_FIXED_RATIO_CLK(name, p, r) \ +static SH_FIXED_RATIO_CLKg(name, p, r) + +#define SH_FIXED_RATIO_CLK_SET(name, p, m, d) \ + SH_CLK_RATIO(name, m, d); \ + SH_FIXED_RATIO_CLK(name, p, name) + +#define SH_CLK_SET_RATIO(p, m, d) \ +do { \ + (p)->mul = m; \ + (p)->div = d; \ +} while (0) + +#endif diff --git a/arch/arm/mach-shmobile/common.h b/arch/arm/mach-shmobile/common.h new file mode 100644 index 000000000..afc60bad6 --- /dev/null +++ b/arch/arm/mach-shmobile/common.h @@ -0,0 +1,48 @@ +#ifndef __ARCH_MACH_COMMON_H +#define __ARCH_MACH_COMMON_H + +extern void shmobile_earlytimer_init(void); +extern void shmobile_init_delay(void); +struct twd_local_timer; +extern void shmobile_setup_console(void); +extern void shmobile_boot_vector(void); +extern unsigned long shmobile_boot_fn; +extern unsigned long shmobile_boot_arg; +extern unsigned long shmobile_boot_size; +extern void shmobile_smp_boot(void); +extern void shmobile_smp_sleep(void); +extern void shmobile_smp_hook(unsigned int cpu, unsigned long fn, + unsigned long arg); +extern int shmobile_smp_cpu_disable(unsigned int cpu); +extern void shmobile_invalidate_start(void); +extern void shmobile_boot_scu(void); +extern void shmobile_smp_scu_prepare_cpus(unsigned int max_cpus); +extern void shmobile_smp_scu_cpu_die(unsigned int cpu); +extern int shmobile_smp_scu_cpu_kill(unsigned int cpu); +struct clk; +extern int shmobile_clk_init(void); +extern struct platform_suspend_ops shmobile_suspend_ops; + +#ifdef CONFIG_SUSPEND +int shmobile_suspend_init(void); +void shmobile_smp_apmu_suspend_init(void); +#else +static inline int shmobile_suspend_init(void) { return 0; } +static inline void shmobile_smp_apmu_suspend_init(void) { } +#endif + +#ifdef CONFIG_CPU_FREQ +int shmobile_cpufreq_init(void); +#else +static inline int shmobile_cpufreq_init(void) { return 0; } +#endif + +extern void __iomem *shmobile_scu_base; + +static inline void __init shmobile_init_late(void) +{ + shmobile_suspend_init(); + shmobile_cpufreq_init(); +} + +#endif /* __ARCH_MACH_COMMON_H */ diff --git a/arch/arm/mach-shmobile/console.c b/arch/arm/mach-shmobile/console.c new file mode 100644 index 000000000..e329ccbd0 --- /dev/null +++ b/arch/arm/mach-shmobile/console.c @@ -0,0 +1,27 @@ +/* + * SH-Mobile Console + * + * Copyright (C) 2010 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <asm/mach/map.h> +#include "common.h" + +void __init shmobile_setup_console(void) +{ + parse_early_param(); + + /* Let earlyprintk output early console messages */ + early_platform_driver_probe("earlyprintk", 1, 1); +} diff --git a/arch/arm/mach-shmobile/cpufreq.c b/arch/arm/mach-shmobile/cpufreq.c new file mode 100644 index 000000000..57fbff024 --- /dev/null +++ b/arch/arm/mach-shmobile/cpufreq.c @@ -0,0 +1,17 @@ +/* + * CPUFreq support code for SH-Mobile ARM + * + * Copyright (C) 2014 Gaku Inami + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/platform_device.h> + +int __init shmobile_cpufreq_init(void) +{ + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); + return 0; +} diff --git a/arch/arm/mach-shmobile/dma-register.h b/arch/arm/mach-shmobile/dma-register.h new file mode 100644 index 000000000..52a2f66e6 --- /dev/null +++ b/arch/arm/mach-shmobile/dma-register.h @@ -0,0 +1,84 @@ +/* + * SH-ARM CPU-specific DMA definitions, used by both DMA drivers + * + * Copyright (C) 2012 Renesas Solutions Corp + * + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * Based on arch/sh/include/cpu-sh4/cpu/dma-register.h + * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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. + */ + +#ifndef DMA_REGISTER_H +#define DMA_REGISTER_H + +/* + * Direct Memory Access Controller + */ + +/* Transmit sizes and respective CHCR register values */ +enum { + XMIT_SZ_8BIT = 0, + XMIT_SZ_16BIT = 1, + XMIT_SZ_32BIT = 2, + XMIT_SZ_64BIT = 7, + XMIT_SZ_128BIT = 3, + XMIT_SZ_256BIT = 4, + XMIT_SZ_512BIT = 5, +}; + +/* log2(size / 8) - used to calculate number of transfers */ +static const unsigned int dma_ts_shift[] = { + [XMIT_SZ_8BIT] = 0, + [XMIT_SZ_16BIT] = 1, + [XMIT_SZ_32BIT] = 2, + [XMIT_SZ_64BIT] = 3, + [XMIT_SZ_128BIT] = 4, + [XMIT_SZ_256BIT] = 5, + [XMIT_SZ_512BIT] = 6, +}; + +#define TS_LOW_BIT 0x3 /* --xx */ +#define TS_HI_BIT 0xc /* xx-- */ + +#define TS_LOW_SHIFT (3) +#define TS_HI_SHIFT (20 - 2) /* 2 bits for shifted low TS */ + +#define TS_INDEX2VAL(i) \ + ((((i) & TS_LOW_BIT) << TS_LOW_SHIFT) |\ + (((i) & TS_HI_BIT) << TS_HI_SHIFT)) + +#define CHCR_TX(xmit_sz) (DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL((xmit_sz))) +#define CHCR_RX(xmit_sz) (DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL((xmit_sz))) + + +/* + * USB High-Speed DMAC + */ +/* Transmit sizes and respective CHCR register values */ +enum { + USBTS_XMIT_SZ_8BYTE = 0, + USBTS_XMIT_SZ_16BYTE = 1, + USBTS_XMIT_SZ_32BYTE = 2, +}; + +/* log2(size / 8) - used to calculate number of transfers */ +static const unsigned int dma_usbts_shift[] = { + [USBTS_XMIT_SZ_8BYTE] = 3, + [USBTS_XMIT_SZ_16BYTE] = 4, + [USBTS_XMIT_SZ_32BYTE] = 5, +}; + +#define USBTS_LOW_BIT 0x3 /* --xx */ +#define USBTS_HI_BIT 0x0 /* ---- */ + +#define USBTS_LOW_SHIFT 6 +#define USBTS_HI_SHIFT 0 + +#define USBTS_INDEX2VAL(i) (((i) & 3) << 6) + +#endif /* DMA_REGISTER_H */ diff --git a/arch/arm/mach-shmobile/headsmp-scu.S b/arch/arm/mach-shmobile/headsmp-scu.S new file mode 100644 index 000000000..69df8bfac --- /dev/null +++ b/arch/arm/mach-shmobile/headsmp-scu.S @@ -0,0 +1,46 @@ +/* + * Shared SCU setup for mach-shmobile + * + * Copyright (C) 2012 Bastian Hecht + * + * 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; either version 2 of + * the License, or (at your option) any later version. + * + * 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. + */ + +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/memory.h> + +/* + * Boot code for secondary CPUs. + * + * First we turn on L1 cache coherency for our CPU. Then we jump to + * shmobile_invalidate_start that invalidates the cache and hands over control + * to the common ARM startup code. + */ +ENTRY(shmobile_boot_scu) + @ r0 = SCU base address + mrc p15, 0, r1, c0, c0, 5 @ read MIPDR + and r1, r1, #3 @ mask out cpu ID + lsl r1, r1, #3 @ we will shift by cpu_id * 8 bits + ldr r2, [r0, #8] @ SCU Power Status Register + mov r3, #3 + lsl r3, r3, r1 + bic r2, r2, r3 @ Clear bits of our CPU (Run Mode) + str r2, [r0, #8] @ write back + + b shmobile_invalidate_start +ENDPROC(shmobile_boot_scu) + + .text + .align 2 + .globl shmobile_scu_base +shmobile_scu_base: + .space 4 diff --git a/arch/arm/mach-shmobile/headsmp.S b/arch/arm/mach-shmobile/headsmp.S new file mode 100644 index 000000000..50c491567 --- /dev/null +++ b/arch/arm/mach-shmobile/headsmp.S @@ -0,0 +1,98 @@ +/* + * SMP support for R-Mobile / SH-Mobile + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2010 Takashi Yoshii + * + * Based on vexpress, Copyright (c) 2003 ARM Limited, All Rights Reserved + * + * 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/init.h> +#include <linux/linkage.h> +#include <linux/threads.h> +#include <asm/assembler.h> +#include <asm/memory.h> + +#ifdef CONFIG_SMP +ENTRY(shmobile_invalidate_start) + bl v7_invalidate_l1 + b secondary_startup +ENDPROC(shmobile_invalidate_start) +#endif + +/* + * Reset vector for secondary CPUs. + * This will be mapped at address 0 by SBAR register. + * We need _long_ jump to the physical address. + */ + .arm + .align 12 +ENTRY(shmobile_boot_vector) + ldr r0, 2f + ldr r1, 1f + bx r1 + +ENDPROC(shmobile_boot_vector) + + .align 2 + .globl shmobile_boot_fn +shmobile_boot_fn: +1: .space 4 + .globl shmobile_boot_arg +shmobile_boot_arg: +2: .space 4 + .globl shmobile_boot_size +shmobile_boot_size: + .long . - shmobile_boot_vector + +/* + * Per-CPU SMP boot function/argument selection code based on MPIDR + */ + +ENTRY(shmobile_smp_boot) + @ r0 = MPIDR_HWID_BITMASK + mrc p15, 0, r1, c0, c0, 5 @ r1 = MPIDR + and r0, r1, r0 @ r0 = cpu_logical_map() value + mov r1, #0 @ r1 = CPU index + adr r5, 1f @ array of per-cpu mpidr values + adr r6, 2f @ array of per-cpu functions + adr r7, 3f @ array of per-cpu arguments + +shmobile_smp_boot_find_mpidr: + ldr r8, [r5, r1, lsl #2] + cmp r8, r0 + bne shmobile_smp_boot_next + + ldr r9, [r6, r1, lsl #2] + cmp r9, #0 + bne shmobile_smp_boot_found + +shmobile_smp_boot_next: + add r1, r1, #1 + cmp r1, #NR_CPUS + blo shmobile_smp_boot_find_mpidr + + b shmobile_smp_sleep + +shmobile_smp_boot_found: + ldr r0, [r7, r1, lsl #2] + ret r9 +ENDPROC(shmobile_smp_boot) + +ENTRY(shmobile_smp_sleep) + wfi + b shmobile_smp_boot +ENDPROC(shmobile_smp_sleep) + + .globl shmobile_smp_mpidr +shmobile_smp_mpidr: +1: .space NR_CPUS * 4 + .globl shmobile_smp_fn +shmobile_smp_fn: +2: .space NR_CPUS * 4 + .globl shmobile_smp_arg +shmobile_smp_arg: +3: .space NR_CPUS * 4 diff --git a/arch/arm/mach-shmobile/include/mach/head-kzm9g.txt b/arch/arm/mach-shmobile/include/mach/head-kzm9g.txt new file mode 100644 index 000000000..9531f46a8 --- /dev/null +++ b/arch/arm/mach-shmobile/include/mach/head-kzm9g.txt @@ -0,0 +1,410 @@ +LIST "KZM9G low-level initialization routine." +LIST "Adapted from u-boot KZM9G support code." + +LIST "Copyright (C) 2013 Ulrich Hecht" + +LIST "This program is free software; you can redistribute it and/or modify" +LIST "it under the terms of the GNU General Public License version 2 as" +LIST "published by the Free Software Foundation." + +LIST "This program is distributed in the hope that it will be useful," +LIST "but WITHOUT ANY WARRANTY; without even the implied warranty of" +LIST "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" +LIST "GNU General Public License for more details." + + +LIST "Register definitions:" + +LIST "Secure control register" +#define LIFEC_SEC_SRC (0xE6110008) + +LIST "RWDT" +#define RWDT_BASE (0xE6020000) +#define RWTCSRA0 (RWDT_BASE + 0x04) + +LIST "HPB Semaphore Control Registers" +#define HPBSCR_BASE (0xE6000000) +#define HPBCTRL6 (HPBSCR_BASE + 0x1030) + +#define SBSC1_BASE (0xFE400000) +#define SDCR0A (SBSC1_BASE + 0x0008) +#define SDCR1A (SBSC1_BASE + 0x000C) +#define SDPCRA (SBSC1_BASE + 0x0010) +#define SDCR0SA (SBSC1_BASE + 0x0018) +#define SDCR1SA (SBSC1_BASE + 0x001C) +#define RTCSRA (SBSC1_BASE + 0x0020) +#define RTCORA (SBSC1_BASE + 0x0028) +#define RTCORHA (SBSC1_BASE + 0x002C) +#define SDWCRC0A (SBSC1_BASE + 0x0040) +#define SDWCRC1A (SBSC1_BASE + 0x0044) +#define SDWCR00A (SBSC1_BASE + 0x0048) +#define SDWCR01A (SBSC1_BASE + 0x004C) +#define SDWCR10A (SBSC1_BASE + 0x0050) +#define SDWCR11A (SBSC1_BASE + 0x0054) +#define SDWCR2A (SBSC1_BASE + 0x0060) +#define SDWCRC2A (SBSC1_BASE + 0x0064) +#define ZQCCRA (SBSC1_BASE + 0x0068) +#define SDMRACR0A (SBSC1_BASE + 0x0084) +#define SDMRTMPCRA (SBSC1_BASE + 0x008C) +#define SDMRTMPMSKA (SBSC1_BASE + 0x0094) +#define SDGENCNTA (SBSC1_BASE + 0x009C) +#define SDDRVCR0A (SBSC1_BASE + 0x00B4) +#define DLLCNT0A (SBSC1_BASE + 0x0354) + +#define SDMRA1 (0xFE500000) +#define SDMRA2 (0xFE5C0000) +#define SDMRA3 (0xFE504000) + +#define SBSC2_BASE (0xFB400000) +#define SDCR0B (SBSC2_BASE + 0x0008) +#define SDCR1B (SBSC2_BASE + 0x000C) +#define SDPCRB (SBSC2_BASE + 0x0010) +#define SDCR0SB (SBSC2_BASE + 0x0018) +#define SDCR1SB (SBSC2_BASE + 0x001C) +#define RTCSRB (SBSC2_BASE + 0x0020) +#define RTCORB (SBSC2_BASE + 0x0028) +#define RTCORHB (SBSC2_BASE + 0x002C) +#define SDWCRC0B (SBSC2_BASE + 0x0040) +#define SDWCRC1B (SBSC2_BASE + 0x0044) +#define SDWCR00B (SBSC2_BASE + 0x0048) +#define SDWCR01B (SBSC2_BASE + 0x004C) +#define SDWCR10B (SBSC2_BASE + 0x0050) +#define SDWCR11B (SBSC2_BASE + 0x0054) +#define SDPDCR0B (SBSC2_BASE + 0x0058) +#define SDWCR2B (SBSC2_BASE + 0x0060) +#define SDWCRC2B (SBSC2_BASE + 0x0064) +#define ZQCCRB (SBSC2_BASE + 0x0068) +#define SDMRACR0B (SBSC2_BASE + 0x0084) +#define SDMRTMPCRB (SBSC2_BASE + 0x008C) +#define SDMRTMPMSKB (SBSC2_BASE + 0x0094) +#define SDGENCNTB (SBSC2_BASE + 0x009C) +#define DPHYCNT0B (SBSC2_BASE + 0x00A0) +#define DPHYCNT1B (SBSC2_BASE + 0x00A4) +#define DPHYCNT2B (SBSC2_BASE + 0x00A8) +#define SDDRVCR0B (SBSC2_BASE + 0x00B4) +#define DLLCNT0B (SBSC2_BASE + 0x0354) + +#define SDMRB1 (0xFB500000) +#define SDMRB2 (0xFB5C0000) +#define SDMRB3 (0xFB504000) + +#define CPG_BASE (0xE6150000) +#define FRQCRA (CPG_BASE + 0x0000) +#define FRQCRB (CPG_BASE + 0x0004) +#define FRQCRD (CPG_BASE + 0x00E4) +#define VCLKCR1 (CPG_BASE + 0x0008) +#define VCLKCR2 (CPG_BASE + 0x000C) +#define VCLKCR3 (CPG_BASE + 0x001C) +#define ZBCKCR (CPG_BASE + 0x0010) +#define FLCKCR (CPG_BASE + 0x0014) +#define SD0CKCR (CPG_BASE + 0x0074) +#define SD1CKCR (CPG_BASE + 0x0078) +#define SD2CKCR (CPG_BASE + 0x007C) +#define FSIACKCR (CPG_BASE + 0x0018) +#define SUBCKCR (CPG_BASE + 0x0080) +#define SPUACKCR (CPG_BASE + 0x0084) +#define SPUVCKCR (CPG_BASE + 0x0094) +#define MSUCKCR (CPG_BASE + 0x0088) +#define HSICKCR (CPG_BASE + 0x008C) +#define FSIBCKCR (CPG_BASE + 0x0090) +#define MFCK1CR (CPG_BASE + 0x0098) +#define MFCK2CR (CPG_BASE + 0x009C) +#define DSITCKCR (CPG_BASE + 0x0060) +#define DSI0PCKCR (CPG_BASE + 0x0064) +#define DSI1PCKCR (CPG_BASE + 0x0068) +#define DSI0PHYCR (CPG_BASE + 0x006C) +#define DVFSCR3 (CPG_BASE + 0x0174) +#define DVFSCR4 (CPG_BASE + 0x0178) +#define DVFSCR5 (CPG_BASE + 0x017C) +#define MPMODE (CPG_BASE + 0x00CC) + +#define PLLECR (CPG_BASE + 0x00D0) +#define PLL0CR (CPG_BASE + 0x00D8) +#define PLL1CR (CPG_BASE + 0x0028) +#define PLL2CR (CPG_BASE + 0x002C) +#define PLL3CR (CPG_BASE + 0x00DC) +#define PLL0STPCR (CPG_BASE + 0x00F0) +#define PLL1STPCR (CPG_BASE + 0x00C8) +#define PLL2STPCR (CPG_BASE + 0x00F8) +#define PLL3STPCR (CPG_BASE + 0x00FC) +#define RMSTPCR0 (CPG_BASE + 0x0110) +#define RMSTPCR1 (CPG_BASE + 0x0114) +#define RMSTPCR2 (CPG_BASE + 0x0118) +#define RMSTPCR3 (CPG_BASE + 0x011C) +#define RMSTPCR4 (CPG_BASE + 0x0120) +#define RMSTPCR5 (CPG_BASE + 0x0124) +#define SMSTPCR0 (CPG_BASE + 0x0130) +#define SMSTPCR2 (CPG_BASE + 0x0138) +#define SMSTPCR3 (CPG_BASE + 0x013C) +#define CPGXXCR4 (CPG_BASE + 0x0150) +#define SRCR0 (CPG_BASE + 0x80A0) +#define SRCR2 (CPG_BASE + 0x80B0) +#define SRCR3 (CPG_BASE + 0x80A8) +#define VREFCR (CPG_BASE + 0x00EC) +#define PCLKCR (CPG_BASE + 0x1020) + +#define PORT32CR (0xE6051020) +#define PORT33CR (0xE6051021) +#define PORT34CR (0xE6051022) +#define PORT35CR (0xE6051023) + +LIST "DRAM initialization code:" + +EW RWTCSRA0, 0xA507 + +ED_AND LIFEC_SEC_SRC, 0xFFFF7FFF + +ED_AND SMSTPCR3,0xFFFF7FFF +ED_AND SRCR3, 0xFFFF7FFF +ED_AND SMSTPCR2,0xFFFBFFFF +ED_AND SRCR2, 0xFFFBFFFF +ED PLLECR, 0x00000000 + +WAIT_MASK PLLECR, 0x00000F00, 0x00000000 +WAIT_MASK FRQCRB, 0x80000000, 0x00000000 + +ED PLL0CR, 0x2D000000 +ED PLL1CR, 0x17100000 +ED FRQCRB, 0x96235880 +WAIT_MASK FRQCRB, 0x80000000, 0x00000000 + +ED FLCKCR, 0x0000000B +ED_AND SMSTPCR0, 0xFFFFFFFD + +ED_AND SRCR0, 0xFFFFFFFD +ED 0xE6001628, 0x514 +ED 0xE6001648, 0x514 +ED 0xE6001658, 0x514 +ED 0xE6001678, 0x514 + +ED DVFSCR4, 0x00092000 +ED DVFSCR5, 0x000000DC +ED PLLECR, 0x00000000 +WAIT_MASK PLLECR, 0x00000F00, 0x00000000 + +ED FRQCRA, 0x0012453C +ED FRQCRB, 0x80431350 +WAIT_MASK FRQCRB, 0x80000000, 0x00000000 +ED FRQCRD, 0x00000B0B +WAIT_MASK FRQCRD, 0x80000000, 0x00000000 + +ED PCLKCR, 0x00000003 +ED VCLKCR1, 0x0000012F +ED VCLKCR2, 0x00000119 +ED VCLKCR3, 0x00000119 +ED ZBCKCR, 0x00000002 +ED FLCKCR, 0x00000005 +ED SD0CKCR, 0x00000080 +ED SD1CKCR, 0x00000080 +ED SD2CKCR, 0x00000080 +ED FSIACKCR, 0x0000003F +ED FSIBCKCR, 0x0000003F +ED SUBCKCR, 0x00000080 +ED SPUACKCR, 0x0000000B +ED SPUVCKCR, 0x0000000B +ED MSUCKCR, 0x0000013F +ED HSICKCR, 0x00000080 +ED MFCK1CR, 0x0000003F +ED MFCK2CR, 0x0000003F +ED DSITCKCR, 0x00000107 +ED DSI0PCKCR, 0x00000313 +ED DSI1PCKCR, 0x0000130D +ED DSI0PHYCR, 0x2A800E0E +ED PLL0CR, 0x1E000000 +ED PLL0CR, 0x2D000000 +ED PLL1CR, 0x17100000 +ED PLL2CR, 0x27000080 +ED PLL3CR, 0x1D000000 +ED PLL0STPCR, 0x00080000 +ED PLL1STPCR, 0x000120C0 +ED PLL2STPCR, 0x00012000 +ED PLL3STPCR, 0x00000030 +ED PLLECR, 0x0000000B +WAIT_MASK PLLECR, 0x00000B00, 0x00000B00 + +ED DVFSCR3, 0x000120F0 +ED MPMODE, 0x00000020 +ED VREFCR, 0x0000028A +ED RMSTPCR0, 0xE4628087 +ED RMSTPCR1, 0xFFFFFFFF +ED RMSTPCR2, 0x53FFFFFF +ED RMSTPCR3, 0xFFFFFFFF +ED RMSTPCR4, 0x00800D3D +ED RMSTPCR5, 0xFFFFF3FF +ED SMSTPCR2, 0x00000000 +ED SRCR2, 0x00040000 +ED_AND PLLECR, 0xFFFFFFF7 +WAIT_MASK PLLECR, 0x00000800, 0x00000000 + +LIST "set SBSC operational" +ED HPBCTRL6, 0x00000001 +WAIT_MASK HPBCTRL6, 0x00000001, 0x00000001 + +LIST "set SBSC operating frequency" +ED FRQCRD, 0x00001414 +WAIT_MASK FRQCRD, 0x80000000, 0x00000000 +ED PLL3CR, 0x1D000000 +ED_OR PLLECR, 0x00000008 +WAIT_MASK PLLECR, 0x00000800, 0x00000800 + +LIST "enable DLL oscillation in DDRPHY" +ED_OR DLLCNT0A, 0x00000002 + +LIST "wait >= 100 ns" +ED SDGENCNTA, 0x00000005 +WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000 + +LIST "target LPDDR2 device settings" +ED SDCR0A, 0xACC90159 +ED SDCR1A, 0x00010059 +ED SDWCRC0A, 0x50874114 +ED SDWCRC1A, 0x33199B37 +ED SDWCRC2A, 0x008F2313 +ED SDWCR00A, 0x31020707 +ED SDWCR01A, 0x0017040A +ED SDWCR10A, 0x31020707 +ED SDWCR11A, 0x0017040A + +ED SDDRVCR0A, 0x055557ff + +ED SDWCR2A, 0x30000000 + +LIST "drive CKE high" +ED_OR SDPCRA, 0x00000080 +WAIT_MASK SDPCRA, 0x00000080, 0x00000080 + +LIST "wait >= 200 us" +ED SDGENCNTA, 0x00002710 +WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000 + +LIST "issue reset command to LPDDR2 device" +ED SDMRACR0A, 0x0000003F +ED SDMRA1, 0x00000000 + +LIST "wait >= 10 (or 1) us (docs inconsistent)" +ED SDGENCNTA, 0x000001F4 +WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000 + +LIST "MRW ZS initialization calibration command" +ED SDMRACR0A, 0x0000FF0A +ED SDMRA3, 0x00000000 + +LIST "wait >= 1 us" +ED SDGENCNTA, 0x00000032 +WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000 + +LIST "specify operating mode in LPDDR2" +ED SDMRACR0A, 0x00002201 +ED SDMRA1, 0x00000000 +ED SDMRACR0A, 0x00000402 +ED SDMRA1, 0x00000000 +ED SDMRACR0A, 0x00000203 +ED SDMRA1, 0x00000000 + +LIST "initialize DDR interface" +ED SDMRA2, 0x00000000 + +LIST "temperature sensor control" +ED SDMRTMPCRA, 0x88800004 +ED SDMRTMPMSKA,0x00000004 + +LIST "auto-refreshing control" +ED RTCORA, 0xA55A0032 +ED RTCORHA, 0xA55A000C +ED RTCSRA, 0xA55A2048 + +ED_OR SDCR0A, 0x00000800 +ED_OR SDCR1A, 0x00000400 + +LIST "auto ZQ calibration control" +ED ZQCCRA, 0xFFF20000 + +ED_OR DLLCNT0B, 0x00000002 +ED SDGENCNTB, 0x00000005 +WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000 + +ED SDCR0B, 0xACC90159 +ED SDCR1B, 0x00010059 +ED SDWCRC0B, 0x50874114 +ED SDWCRC1B, 0x33199B37 +ED SDWCRC2B, 0x008F2313 +ED SDWCR00B, 0x31020707 +ED SDWCR01B, 0x0017040A +ED SDWCR10B, 0x31020707 +ED SDWCR11B, 0x0017040A +ED SDDRVCR0B, 0x055557ff +ED SDWCR2B, 0x30000000 +ED_OR SDPCRB, 0x00000080 +WAIT_MASK SDPCRB, 0x00000080, 0x00000080 + +ED SDGENCNTB, 0x00002710 +WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000 +ED SDMRACR0B, 0x0000003F + +LIST "upstream u-boot writes to SDMRA1A for both SBSC 1 and 2, which does" +LIST "not seem to make a lot of sense..." +ED SDMRB1, 0x00000000 + +ED SDGENCNTB, 0x000001F4 +WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000 + +ED SDMRACR0B, 0x0000FF0A +ED SDMRB3, 0x00000000 +ED SDGENCNTB, 0x00000032 +WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000 + +ED SDMRACR0B, 0x00002201 +ED SDMRB1, 0x00000000 +ED SDMRACR0B, 0x00000402 +ED SDMRB1, 0x00000000 +ED SDMRACR0B, 0x00000203 +ED SDMRB1, 0x00000000 +ED SDMRB2, 0x00000000 +ED SDMRTMPCRB, 0x88800004 +ED SDMRTMPMSKB, 0x00000004 +ED RTCORB, 0xA55A0032 +ED RTCORHB, 0xA55A000C +ED RTCSRB, 0xA55A2048 +ED_OR SDCR0B, 0x00000800 +ED_OR SDCR1B, 0x00000400 +ED ZQCCRB, 0xFFF20000 +ED_OR SDPDCR0B, 0x00030000 +ED DPHYCNT1B, 0xA5390000 +ED DPHYCNT0B, 0x00001200 +ED DPHYCNT1B, 0x07CE0000 +ED DPHYCNT0B, 0x00001247 +WAIT_MASK DPHYCNT2B, 0xFFFFFFFF, 0x07CE0000 + +ED_AND SDPDCR0B, 0xFFFCFFFF + +ED FRQCRD, 0x00000B0B +WAIT_MASK FRQCRD, 0x80000000, 0x00000000 + +ED CPGXXCR4, 0xfffffffc + +LIST "Setup SCIF4 / workaround" +EB PORT32CR, 0x12 +EB PORT33CR, 0x22 +EB PORT34CR, 0x12 +EB PORT35CR, 0x22 + +EW 0xE6C80000, 0 +EB 0xE6C80004, 0x19 +EW 0xE6C80008, 0x0030 +EW 0xE6C80018, 0 +EW 0xE6C80030, 0x0014 + +LIST "Magic to avoid hangs and corruption on DRAM writes." + +LIST "It has been observed that the system would most often hang while" +LIST "decompressing the kernel, and if it didn't it would always write" +LIST "a corrupt image to DRAM." +LIST "This problem does not occur in u-boot, and the reason is that" +LIST "u-boot performs an additional cache invalidation after setting up" +LIST "the DRAM controller. Such an invalidation should not be necessary at" +LIST "this point, and attempts at removing parts of the routine to arrive" +LIST "at the minimal snippet of code necessary to avoid the DRAM stability" +LIST "problem yielded the following:" + +MRC p15, 0, r0, c1, c0, 0 +MCR p15, 0, r0, c1, c0, 0 diff --git a/arch/arm/mach-shmobile/include/mach/irqs.h b/arch/arm/mach-shmobile/include/mach/irqs.h new file mode 100644 index 000000000..5aee83f07 --- /dev/null +++ b/arch/arm/mach-shmobile/include/mach/irqs.h @@ -0,0 +1,10 @@ +#ifndef __ASM_MACH_IRQS_H +#define __ASM_MACH_IRQS_H + +/* Stuck here until drivers/pinctl/sh-pfc gets rid of legacy code */ + +/* External IRQ pins */ +#define IRQPIN_BASE 2000 +#define irq_pin(nr) ((nr) + IRQPIN_BASE) + +#endif /* __ASM_MACH_IRQS_H */ diff --git a/arch/arm/mach-shmobile/include/mach/zboot.h b/arch/arm/mach-shmobile/include/mach/zboot.h new file mode 100644 index 000000000..175ee0546 --- /dev/null +++ b/arch/arm/mach-shmobile/include/mach/zboot.h @@ -0,0 +1,19 @@ +#ifndef ZBOOT_H +#define ZBOOT_H + +#include <mach/zboot_macros.h> + +/************************************************** + * + * board specific settings + * + **************************************************/ + +#ifdef CONFIG_MACH_KZM9G +#define MEMORY_START 0x43000000 +#include "mach/head-kzm9g.txt" +#else +#error "unsupported board." +#endif + +#endif /* ZBOOT_H */ diff --git a/arch/arm/mach-shmobile/include/mach/zboot_macros.h b/arch/arm/mach-shmobile/include/mach/zboot_macros.h new file mode 100644 index 000000000..14fd3d538 --- /dev/null +++ b/arch/arm/mach-shmobile/include/mach/zboot_macros.h @@ -0,0 +1,108 @@ +#ifndef __ZBOOT_MACRO_H +#define __ZBOOT_MACRO_H + +/* The LIST command is used to include comments in the script */ +.macro LIST comment +.endm + +/* The ED command is used to write a 32-bit word */ +.macro ED, addr, data + LDR r0, 1f + LDR r1, 2f + STR r1, [r0] + B 3f +1 : .long \addr +2 : .long \data +3 : +.endm + +/* The EW command is used to write a 16-bit word */ +.macro EW, addr, data + LDR r0, 1f + LDR r1, 2f + STRH r1, [r0] + B 3f +1 : .long \addr +2 : .long \data +3 : +.endm + +/* The EB command is used to write an 8-bit word */ +.macro EB, addr, data + LDR r0, 1f + LDR r1, 2f + STRB r1, [r0] + B 3f +1 : .long \addr +2 : .long \data +3 : +.endm + +/* The WAIT command is used to delay the execution */ +.macro WAIT, time, reg + LDR r1, 1f + LDR r0, 2f + STR r0, [r1] +10 : + LDR r0, [r1] + CMP r0, #0x00000000 + BNE 10b + NOP + B 3f +1 : .long \reg +2 : .long \time * 100 +3 : +.endm + +/* The DD command is used to read a 32-bit word */ +.macro DD, start, end + LDR r1, 1f + B 2f +1 : .long \start +2 : +.endm + +/* loop until a given value has been read (with mask) */ +.macro WAIT_MASK, addr, data, cmp + LDR r0, 2f + LDR r1, 3f + LDR r2, 4f +1: + LDR r3, [r0, #0] + AND r3, r1, r3 + CMP r2, r3 + BNE 1b + B 5f +2: .long \addr +3: .long \data +4: .long \cmp +5: +.endm + +/* read 32-bit value from addr, "or" an immediate and write back */ +.macro ED_OR, addr, data + LDR r4, 1f + LDR r5, 2f + LDR r6, [r4] + ORR r5, r6, r5 + STR r5, [r4] + B 3f +1: .long \addr +2: .long \data +3: +.endm + +/* read 32-bit value from addr, "and" an immediate and write back */ +.macro ED_AND, addr, data + LDR r4, 1f + LDR r5, 2f + LDR r6, [r4] + AND r5, r6, r5 + STR r5, [r4] + B 3f +1: .long \addr +2: .long \data +3: +.endm + +#endif /* __ZBOOT_MACRO_H */ diff --git a/arch/arm/mach-shmobile/intc-sh73a0.c b/arch/arm/mach-shmobile/intc-sh73a0.c new file mode 100644 index 000000000..fd63ae653 --- /dev/null +++ b/arch/arm/mach-shmobile/intc-sh73a0.c @@ -0,0 +1,337 @@ +/* + * sh73a0 processor support - INTC hardware block + * + * Copyright (C) 2010 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/irqchip.h> +#include <linux/irqchip/arm-gic.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include "intc.h" +#include "irqs.h" +#include "sh73a0.h" + +enum { + UNUSED = 0, + + /* interrupt sources INTCS */ + PINTCS_PINT1, PINTCS_PINT2, + RTDMAC_0_DEI0, RTDMAC_0_DEI1, RTDMAC_0_DEI2, RTDMAC_0_DEI3, + CEU, MFI, BBIF2, VPU, TSIF1, _3DG_SGX543, _2DDMAC_2DDM0, + RTDMAC_1_DEI4, RTDMAC_1_DEI5, RTDMAC_1_DADERR, + KEYSC_KEY, VINT, MSIOF, + TMU0_TUNI00, TMU0_TUNI01, TMU0_TUNI02, + CMT0, TSIF0, CMT2, LMB, MSUG, MSU_MSU, MSU_MSU2, + CTI, RWDT0, ICB, PEP, ASA, JPU_JPEG, LCDC, LCRC, + RTDMAC_2_DEI6, RTDMAC_2_DEI7, RTDMAC_2_DEI8, RTDMAC_2_DEI9, + RTDMAC_3_DEI10, RTDMAC_3_DEI11, + FRC, GCU, LCDC1, CSIRX, + DSITX0_DSITX00, DSITX0_DSITX01, + SPU2_SPU0, SPU2_SPU1, FSI, + TMU1_TUNI10, TMU1_TUNI11, TMU1_TUNI12, + TSIF2, CMT4, MFIS2, CPORTS2R, TSG, DMASCH1, SCUW, + VIO60, VIO61, CEU21, CSI21, DSITX1_DSITX10, DSITX1_DSITX11, + DISP, DSRV, EMUX2_EMUX20I, EMUX2_EMUX21I, + MSTIF0_MST00I, MSTIF0_MST01I, MSTIF1_MST10I, MSTIF1_MST11I, + SPUV, + + /* interrupt groups INTCS */ + RTDMAC_0, RTDMAC_1, RTDMAC_2, RTDMAC_3, + DSITX0, SPU2, TMU1, MSU, +}; + +static struct intc_vect intcs_vectors[] = { + INTCS_VECT(PINTCS_PINT1, 0x0600), INTCS_VECT(PINTCS_PINT2, 0x0620), + INTCS_VECT(RTDMAC_0_DEI0, 0x0800), INTCS_VECT(RTDMAC_0_DEI1, 0x0820), + INTCS_VECT(RTDMAC_0_DEI2, 0x0840), INTCS_VECT(RTDMAC_0_DEI3, 0x0860), + INTCS_VECT(CEU, 0x0880), INTCS_VECT(MFI, 0x0900), + INTCS_VECT(BBIF2, 0x0960), INTCS_VECT(VPU, 0x0980), + INTCS_VECT(TSIF1, 0x09a0), INTCS_VECT(_3DG_SGX543, 0x09e0), + INTCS_VECT(_2DDMAC_2DDM0, 0x0a00), + INTCS_VECT(RTDMAC_1_DEI4, 0x0b80), INTCS_VECT(RTDMAC_1_DEI5, 0x0ba0), + INTCS_VECT(RTDMAC_1_DADERR, 0x0bc0), + INTCS_VECT(KEYSC_KEY, 0x0be0), INTCS_VECT(VINT, 0x0c80), + INTCS_VECT(MSIOF, 0x0d20), + INTCS_VECT(TMU0_TUNI00, 0x0e80), INTCS_VECT(TMU0_TUNI01, 0x0ea0), + INTCS_VECT(TMU0_TUNI02, 0x0ec0), + INTCS_VECT(CMT0, 0x0f00), INTCS_VECT(TSIF0, 0x0f20), + INTCS_VECT(CMT2, 0x0f40), INTCS_VECT(LMB, 0x0f60), + INTCS_VECT(MSUG, 0x0f80), + INTCS_VECT(MSU_MSU, 0x0fa0), INTCS_VECT(MSU_MSU2, 0x0fc0), + INTCS_VECT(CTI, 0x0400), INTCS_VECT(RWDT0, 0x0440), + INTCS_VECT(ICB, 0x0480), INTCS_VECT(PEP, 0x04a0), + INTCS_VECT(ASA, 0x04c0), INTCS_VECT(JPU_JPEG, 0x0560), + INTCS_VECT(LCDC, 0x0580), INTCS_VECT(LCRC, 0x05a0), + INTCS_VECT(RTDMAC_2_DEI6, 0x1300), INTCS_VECT(RTDMAC_2_DEI7, 0x1320), + INTCS_VECT(RTDMAC_2_DEI8, 0x1340), INTCS_VECT(RTDMAC_2_DEI9, 0x1360), + INTCS_VECT(RTDMAC_3_DEI10, 0x1380), INTCS_VECT(RTDMAC_3_DEI11, 0x13a0), + INTCS_VECT(FRC, 0x1700), INTCS_VECT(GCU, 0x1760), + INTCS_VECT(LCDC1, 0x1780), INTCS_VECT(CSIRX, 0x17a0), + INTCS_VECT(DSITX0_DSITX00, 0x17c0), INTCS_VECT(DSITX0_DSITX01, 0x17e0), + INTCS_VECT(SPU2_SPU0, 0x1800), INTCS_VECT(SPU2_SPU1, 0x1820), + INTCS_VECT(FSI, 0x1840), + INTCS_VECT(TMU1_TUNI10, 0x1900), INTCS_VECT(TMU1_TUNI11, 0x1920), + INTCS_VECT(TMU1_TUNI12, 0x1940), + INTCS_VECT(TSIF2, 0x1960), INTCS_VECT(CMT4, 0x1980), + INTCS_VECT(MFIS2, 0x1a00), INTCS_VECT(CPORTS2R, 0x1a20), + INTCS_VECT(TSG, 0x1ae0), INTCS_VECT(DMASCH1, 0x1b00), + INTCS_VECT(SCUW, 0x1b40), + INTCS_VECT(VIO60, 0x1b60), INTCS_VECT(VIO61, 0x1b80), + INTCS_VECT(CEU21, 0x1ba0), INTCS_VECT(CSI21, 0x1be0), + INTCS_VECT(DSITX1_DSITX10, 0x1c00), INTCS_VECT(DSITX1_DSITX11, 0x1c20), + INTCS_VECT(DISP, 0x1c40), INTCS_VECT(DSRV, 0x1c60), + INTCS_VECT(EMUX2_EMUX20I, 0x1c80), INTCS_VECT(EMUX2_EMUX21I, 0x1ca0), + INTCS_VECT(MSTIF0_MST00I, 0x1cc0), INTCS_VECT(MSTIF0_MST01I, 0x1ce0), + INTCS_VECT(MSTIF1_MST10I, 0x1d00), INTCS_VECT(MSTIF1_MST11I, 0x1d20), + INTCS_VECT(SPUV, 0x2300), +}; + +static struct intc_group intcs_groups[] __initdata = { + INTC_GROUP(RTDMAC_0, RTDMAC_0_DEI0, RTDMAC_0_DEI1, + RTDMAC_0_DEI2, RTDMAC_0_DEI3), + INTC_GROUP(RTDMAC_1, RTDMAC_1_DEI4, RTDMAC_1_DEI5, RTDMAC_1_DADERR), + INTC_GROUP(RTDMAC_2, RTDMAC_2_DEI6, RTDMAC_2_DEI7, + RTDMAC_2_DEI8, RTDMAC_2_DEI9), + INTC_GROUP(RTDMAC_3, RTDMAC_3_DEI10, RTDMAC_3_DEI11), + INTC_GROUP(TMU1, TMU1_TUNI12, TMU1_TUNI11, TMU1_TUNI10), + INTC_GROUP(DSITX0, DSITX0_DSITX00, DSITX0_DSITX01), + INTC_GROUP(SPU2, SPU2_SPU0, SPU2_SPU1), + INTC_GROUP(MSU, MSU_MSU, MSU_MSU2), +}; + +static struct intc_mask_reg intcs_mask_registers[] = { + { 0xffd20184, 0xffd201c4, 8, /* IMR1SA / IMCR1SA */ + { 0, 0, 0, CEU, + 0, 0, 0, 0 } }, + { 0xffd20188, 0xffd201c8, 8, /* IMR2SA / IMCR2SA */ + { 0, 0, 0, VPU, + BBIF2, 0, 0, MFI } }, + { 0xffd2018c, 0xffd201cc, 8, /* IMR3SA / IMCR3SA */ + { 0, 0, 0, _2DDMAC_2DDM0, + 0, ASA, PEP, ICB } }, + { 0xffd20190, 0xffd201d0, 8, /* IMR4SA / IMCR4SA */ + { 0, 0, 0, CTI, + JPU_JPEG, 0, LCRC, LCDC } }, + { 0xffd20194, 0xffd201d4, 8, /* IMR5SA / IMCR5SA */ + { KEYSC_KEY, RTDMAC_1_DADERR, RTDMAC_1_DEI5, RTDMAC_1_DEI4, + RTDMAC_0_DEI3, RTDMAC_0_DEI2, RTDMAC_0_DEI1, RTDMAC_0_DEI0 } }, + { 0xffd20198, 0xffd201d8, 8, /* IMR6SA / IMCR6SA */ + { 0, 0, MSIOF, 0, + _3DG_SGX543, 0, 0, 0 } }, + { 0xffd2019c, 0xffd201dc, 8, /* IMR7SA / IMCR7SA */ + { 0, TMU0_TUNI02, TMU0_TUNI01, TMU0_TUNI00, + 0, 0, 0, 0 } }, + { 0xffd201a0, 0xffd201e0, 8, /* IMR8SA / IMCR8SA */ + { 0, 0, 0, 0, + 0, MSU_MSU, MSU_MSU2, MSUG } }, + { 0xffd201a4, 0xffd201e4, 8, /* IMR9SA / IMCR9SA */ + { 0, RWDT0, CMT2, CMT0, + 0, 0, 0, 0 } }, + { 0xffd201ac, 0xffd201ec, 8, /* IMR11SA / IMCR11SA */ + { 0, 0, 0, 0, + 0, TSIF1, LMB, TSIF0 } }, + { 0xffd201b0, 0xffd201f0, 8, /* IMR12SA / IMCR12SA */ + { 0, 0, 0, 0, + 0, 0, PINTCS_PINT2, PINTCS_PINT1 } }, + { 0xffd50180, 0xffd501c0, 8, /* IMR0SA3 / IMCR0SA3 */ + { RTDMAC_2_DEI6, RTDMAC_2_DEI7, RTDMAC_2_DEI8, RTDMAC_2_DEI9, + RTDMAC_3_DEI10, RTDMAC_3_DEI11, 0, 0 } }, + { 0xffd50190, 0xffd501d0, 8, /* IMR4SA3 / IMCR4SA3 */ + { FRC, 0, 0, GCU, + LCDC1, CSIRX, DSITX0_DSITX00, DSITX0_DSITX01 } }, + { 0xffd50194, 0xffd501d4, 8, /* IMR5SA3 / IMCR5SA3 */ + { SPU2_SPU0, SPU2_SPU1, FSI, 0, + 0, 0, 0, 0 } }, + { 0xffd50198, 0xffd501d8, 8, /* IMR6SA3 / IMCR6SA3 */ + { TMU1_TUNI10, TMU1_TUNI11, TMU1_TUNI12, 0, + TSIF2, CMT4, 0, 0 } }, + { 0xffd5019c, 0xffd501dc, 8, /* IMR7SA3 / IMCR7SA3 */ + { MFIS2, CPORTS2R, 0, 0, + 0, 0, 0, TSG } }, + { 0xffd501a0, 0xffd501e0, 8, /* IMR8SA3 / IMCR8SA3 */ + { DMASCH1, 0, SCUW, VIO60, + VIO61, CEU21, 0, CSI21 } }, + { 0xffd501a4, 0xffd501e4, 8, /* IMR9SA3 / IMCR9SA3 */ + { DSITX1_DSITX10, DSITX1_DSITX11, DISP, DSRV, + EMUX2_EMUX20I, EMUX2_EMUX21I, MSTIF0_MST00I, MSTIF0_MST01I } }, + { 0xffd501a8, 0xffd501e8, 8, /* IMR10SA3 / IMCR10SA3 */ + { MSTIF0_MST00I, MSTIF0_MST01I, 0, 0, + 0, 0, 0, 0 } }, + { 0xffd60180, 0xffd601c0, 8, /* IMR0SA4 / IMCR0SA4 */ + { SPUV, 0, 0, 0, + 0, 0, 0, 0 } }, +}; + +/* Priority is needed for INTCA to receive the INTCS interrupt */ +static struct intc_prio_reg intcs_prio_registers[] = { + { 0xffd20000, 0, 16, 4, /* IPRAS */ { CTI, 0, _2DDMAC_2DDM0, ICB } }, + { 0xffd20004, 0, 16, 4, /* IPRBS */ { JPU_JPEG, LCDC, 0, LCRC } }, + { 0xffd20008, 0, 16, 4, /* IPRCS */ { BBIF2, 0, 0, 0 } }, + { 0xffd2000c, 0, 16, 4, /* IPRDS */ { PINTCS_PINT1, PINTCS_PINT2, + 0, 0 } }, + { 0xffd20010, 0, 16, 4, /* IPRES */ { RTDMAC_0, CEU, MFI, VPU } }, + { 0xffd20014, 0, 16, 4, /* IPRFS */ { KEYSC_KEY, RTDMAC_1, + CMT2, CMT0 } }, + { 0xffd20018, 0, 16, 4, /* IPRGS */ { TMU0_TUNI00, TMU0_TUNI01, + TMU0_TUNI02, TSIF1 } }, + { 0xffd2001c, 0, 16, 4, /* IPRHS */ { VINT, 0, 0, 0 } }, + { 0xffd20020, 0, 16, 4, /* IPRIS */ { 0, MSIOF, TSIF0, 0 } }, + { 0xffd20024, 0, 16, 4, /* IPRJS */ { 0, _3DG_SGX543, MSUG, MSU } }, + { 0xffd20028, 0, 16, 4, /* IPRKS */ { 0, ASA, LMB, PEP } }, + { 0xffd20030, 0, 16, 4, /* IPRMS */ { 0, 0, 0, RWDT0 } }, + { 0xffd50000, 0, 16, 4, /* IPRAS3 */ { RTDMAC_2, 0, 0, 0 } }, + { 0xffd50004, 0, 16, 4, /* IPRBS3 */ { RTDMAC_3, 0, 0, 0 } }, + { 0xffd50020, 0, 16, 4, /* IPRIS3 */ { FRC, 0, 0, 0 } }, + { 0xffd50024, 0, 16, 4, /* IPRJS3 */ { LCDC1, CSIRX, DSITX0, 0 } }, + { 0xffd50028, 0, 16, 4, /* IPRKS3 */ { SPU2, 0, FSI, 0 } }, + { 0xffd50030, 0, 16, 4, /* IPRMS3 */ { TMU1, 0, 0, TSIF2 } }, + { 0xffd50034, 0, 16, 4, /* IPRNS3 */ { CMT4, 0, 0, 0 } }, + { 0xffd50038, 0, 16, 4, /* IPROS3 */ { MFIS2, CPORTS2R, 0, 0 } }, + { 0xffd50040, 0, 16, 4, /* IPRQS3 */ { DMASCH1, 0, SCUW, VIO60 } }, + { 0xffd50044, 0, 16, 4, /* IPRRS3 */ { VIO61, CEU21, 0, CSI21 } }, + { 0xffd50048, 0, 16, 4, /* IPRSS3 */ { DSITX1_DSITX10, DSITX1_DSITX11, + DISP, DSRV } }, + { 0xffd5004c, 0, 16, 4, /* IPRTS3 */ { EMUX2_EMUX20I, EMUX2_EMUX21I, + MSTIF0_MST00I, MSTIF0_MST01I } }, + { 0xffd50050, 0, 16, 4, /* IPRUS3 */ { MSTIF1_MST10I, MSTIF1_MST11I, + 0, 0 } }, + { 0xffd60000, 0, 16, 4, /* IPRAS4 */ { SPUV, 0, 0, 0 } }, +}; + +static struct resource intcs_resources[] __initdata = { + [0] = { + .start = 0xffd20000, + .end = 0xffd201ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 0xffd50000, + .end = 0xffd501ff, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = 0xffd60000, + .end = 0xffd601ff, + .flags = IORESOURCE_MEM, + } +}; + +static struct intc_desc intcs_desc __initdata = { + .name = "sh73a0-intcs", + .resource = intcs_resources, + .num_resources = ARRAY_SIZE(intcs_resources), + .hw = INTC_HW_DESC(intcs_vectors, intcs_groups, intcs_mask_registers, + intcs_prio_registers, NULL, NULL), +}; + +static struct irqaction sh73a0_intcs_cascade; + +static irqreturn_t sh73a0_intcs_demux(int irq, void *dev_id) +{ + unsigned int evtcodeas = ioread32((void __iomem *)dev_id); + + generic_handle_irq(intcs_evt2irq(evtcodeas)); + + return IRQ_HANDLED; +} + +#define PINTER0_PHYS 0xe69000a0 +#define PINTER1_PHYS 0xe69000a4 +#define PINTER0_VIRT IOMEM(0xe69000a0) +#define PINTER1_VIRT IOMEM(0xe69000a4) +#define PINTRR0 IOMEM(0xe69000d0) +#define PINTRR1 IOMEM(0xe69000d4) + +#define PINT0A_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq)) +#define PINT0B_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq + 8)) +#define PINT0C_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq + 16)) +#define PINT0D_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq + 24)) +#define PINT1E_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT1_IRQ(irq)) + +INTC_PINT(intc_pint0, PINTER0_PHYS, 0xe69000b0, "sh73a0-pint0", \ + INTC_PINT_E(A), INTC_PINT_E(B), INTC_PINT_E(C), INTC_PINT_E(D), \ + INTC_PINT_V(A, PINT0A_IRQ), INTC_PINT_V(B, PINT0B_IRQ), \ + INTC_PINT_V(C, PINT0C_IRQ), INTC_PINT_V(D, PINT0D_IRQ), \ + INTC_PINT_E(A), INTC_PINT_E(B), INTC_PINT_E(C), INTC_PINT_E(D), \ + INTC_PINT_E(A), INTC_PINT_E(B), INTC_PINT_E(C), INTC_PINT_E(D)); + +INTC_PINT(intc_pint1, PINTER1_PHYS, 0xe69000c0, "sh73a0-pint1", \ + INTC_PINT_E(E), INTC_PINT_E_EMPTY, INTC_PINT_E_EMPTY, INTC_PINT_E_EMPTY, \ + INTC_PINT_V(E, PINT1E_IRQ), INTC_PINT_V_NONE, \ + INTC_PINT_V_NONE, INTC_PINT_V_NONE, \ + INTC_PINT_E_NONE, INTC_PINT_E_NONE, INTC_PINT_E_NONE, INTC_PINT_E(E), \ + INTC_PINT_E(E), INTC_PINT_E_NONE, INTC_PINT_E_NONE, INTC_PINT_E_NONE); + +static struct irqaction sh73a0_pint0_cascade; +static struct irqaction sh73a0_pint1_cascade; + +static void pint_demux(void __iomem *rr, void __iomem *er, int base_irq) +{ + unsigned long value = ioread32(rr) & ioread32(er); + int k; + + for (k = 0; k < 32; k++) { + if (value & (1 << (31 - k))) { + generic_handle_irq(base_irq + k); + iowrite32(~(1 << (31 - k)), rr); + } + } +} + +static irqreturn_t sh73a0_pint0_demux(int irq, void *dev_id) +{ + pint_demux(PINTRR0, PINTER0_VIRT, SH73A0_PINT0_IRQ(0)); + return IRQ_HANDLED; +} + +static irqreturn_t sh73a0_pint1_demux(int irq, void *dev_id) +{ + pint_demux(PINTRR1, PINTER1_VIRT, SH73A0_PINT1_IRQ(0)); + return IRQ_HANDLED; +} + +void __init sh73a0_init_irq(void) +{ + void __iomem *gic_dist_base = IOMEM(0xf0001000); + void __iomem *gic_cpu_base = IOMEM(0xf0000100); + void __iomem *intevtsa = ioremap_nocache(0xffd20100, PAGE_SIZE); + + gic_set_irqchip_flags(IRQCHIP_SKIP_SET_WAKE); + gic_init(0, 29, gic_dist_base, gic_cpu_base); + + register_intc_controller(&intcs_desc); + register_intc_controller(&intc_pint0_desc); + register_intc_controller(&intc_pint1_desc); + + /* demux using INTEVTSA */ + sh73a0_intcs_cascade.name = "INTCS cascade"; + sh73a0_intcs_cascade.handler = sh73a0_intcs_demux; + sh73a0_intcs_cascade.dev_id = intevtsa; + setup_irq(gic_spi(50), &sh73a0_intcs_cascade); + + /* PINT pins are sanely tied to the GIC as SPI */ + sh73a0_pint0_cascade.name = "PINT0 cascade"; + sh73a0_pint0_cascade.handler = sh73a0_pint0_demux; + setup_irq(gic_spi(33), &sh73a0_pint0_cascade); + + sh73a0_pint1_cascade.name = "PINT1 cascade"; + sh73a0_pint1_cascade.handler = sh73a0_pint1_demux; + setup_irq(gic_spi(34), &sh73a0_pint1_cascade); +} diff --git a/arch/arm/mach-shmobile/intc.h b/arch/arm/mach-shmobile/intc.h new file mode 100644 index 000000000..40b2ad4ca --- /dev/null +++ b/arch/arm/mach-shmobile/intc.h @@ -0,0 +1,295 @@ +#ifndef __ASM_MACH_INTC_H +#define __ASM_MACH_INTC_H +#include <linux/sh_intc.h> + +#define INTC_IRQ_PINS_ENUM_16L(p) \ + p ## _IRQ0, p ## _IRQ1, p ## _IRQ2, p ## _IRQ3, \ + p ## _IRQ4, p ## _IRQ5, p ## _IRQ6, p ## _IRQ7, \ + p ## _IRQ8, p ## _IRQ9, p ## _IRQ10, p ## _IRQ11, \ + p ## _IRQ12, p ## _IRQ13, p ## _IRQ14, p ## _IRQ15 + +#define INTC_IRQ_PINS_ENUM_16H(p) \ + p ## _IRQ16, p ## _IRQ17, p ## _IRQ18, p ## _IRQ19, \ + p ## _IRQ20, p ## _IRQ21, p ## _IRQ22, p ## _IRQ23, \ + p ## _IRQ24, p ## _IRQ25, p ## _IRQ26, p ## _IRQ27, \ + p ## _IRQ28, p ## _IRQ29, p ## _IRQ30, p ## _IRQ31 + +#define INTC_IRQ_PINS_VECT_16L(p, vect) \ + vect(p ## _IRQ0, 0x0200), vect(p ## _IRQ1, 0x0220), \ + vect(p ## _IRQ2, 0x0240), vect(p ## _IRQ3, 0x0260), \ + vect(p ## _IRQ4, 0x0280), vect(p ## _IRQ5, 0x02a0), \ + vect(p ## _IRQ6, 0x02c0), vect(p ## _IRQ7, 0x02e0), \ + vect(p ## _IRQ8, 0x0300), vect(p ## _IRQ9, 0x0320), \ + vect(p ## _IRQ10, 0x0340), vect(p ## _IRQ11, 0x0360), \ + vect(p ## _IRQ12, 0x0380), vect(p ## _IRQ13, 0x03a0), \ + vect(p ## _IRQ14, 0x03c0), vect(p ## _IRQ15, 0x03e0) + +#define INTC_IRQ_PINS_VECT_16H(p, vect) \ + vect(p ## _IRQ16, 0x3200), vect(p ## _IRQ17, 0x3220), \ + vect(p ## _IRQ18, 0x3240), vect(p ## _IRQ19, 0x3260), \ + vect(p ## _IRQ20, 0x3280), vect(p ## _IRQ21, 0x32a0), \ + vect(p ## _IRQ22, 0x32c0), vect(p ## _IRQ23, 0x32e0), \ + vect(p ## _IRQ24, 0x3300), vect(p ## _IRQ25, 0x3320), \ + vect(p ## _IRQ26, 0x3340), vect(p ## _IRQ27, 0x3360), \ + vect(p ## _IRQ28, 0x3380), vect(p ## _IRQ29, 0x33a0), \ + vect(p ## _IRQ30, 0x33c0), vect(p ## _IRQ31, 0x33e0) + +#define INTC_IRQ_PINS_MASK_16L(p, base) \ + { base + 0x40, base + 0x60, 8, /* INTMSK00A / INTMSKCLR00A */ \ + { p ## _IRQ0, p ## _IRQ1, p ## _IRQ2, p ## _IRQ3, \ + p ## _IRQ4, p ## _IRQ5, p ## _IRQ6, p ## _IRQ7 } }, \ + { base + 0x44, base + 0x64, 8, /* INTMSK10A / INTMSKCLR10A */ \ + { p ## _IRQ8, p ## _IRQ9, p ## _IRQ10, p ## _IRQ11, \ + p ## _IRQ12, p ## _IRQ13, p ## _IRQ14, p ## _IRQ15 } } + +#define INTC_IRQ_PINS_MASK_16H(p, base) \ + { base + 0x48, base + 0x68, 8, /* INTMSK20A / INTMSKCLR20A */ \ + { p ## _IRQ16, p ## _IRQ17, p ## _IRQ18, p ## _IRQ19, \ + p ## _IRQ20, p ## _IRQ21, p ## _IRQ22, p ## _IRQ23 } }, \ + { base + 0x4c, base + 0x6c, 8, /* INTMSK30A / INTMSKCLR30A */ \ + { p ## _IRQ24, p ## _IRQ25, p ## _IRQ26, p ## _IRQ27, \ + p ## _IRQ28, p ## _IRQ29, p ## _IRQ30, p ## _IRQ31 } } + +#define INTC_IRQ_PINS_PRIO_16L(p, base) \ + { base + 0x10, 0, 32, 4, /* INTPRI00A */ \ + { p ## _IRQ0, p ## _IRQ1, p ## _IRQ2, p ## _IRQ3, \ + p ## _IRQ4, p ## _IRQ5, p ## _IRQ6, p ## _IRQ7 } }, \ + { base + 0x14, 0, 32, 4, /* INTPRI10A */ \ + { p ## _IRQ8, p ## _IRQ9, p ## _IRQ10, p ## _IRQ11, \ + p ## _IRQ12, p ## _IRQ13, p ## _IRQ14, p ## _IRQ15 } } + +#define INTC_IRQ_PINS_PRIO_16H(p, base) \ + { base + 0x18, 0, 32, 4, /* INTPRI20A */ \ + { p ## _IRQ16, p ## _IRQ17, p ## _IRQ18, p ## _IRQ19, \ + p ## _IRQ20, p ## _IRQ21, p ## _IRQ22, p ## _IRQ23 } }, \ + { base + 0x1c, 0, 32, 4, /* INTPRI30A */ \ + { p ## _IRQ24, p ## _IRQ25, p ## _IRQ26, p ## _IRQ27, \ + p ## _IRQ28, p ## _IRQ29, p ## _IRQ30, p ## _IRQ31 } } + +#define INTC_IRQ_PINS_SENSE_16L(p, base) \ + { base + 0x00, 32, 4, /* ICR1A */ \ + { p ## _IRQ0, p ## _IRQ1, p ## _IRQ2, p ## _IRQ3, \ + p ## _IRQ4, p ## _IRQ5, p ## _IRQ6, p ## _IRQ7 } }, \ + { base + 0x04, 32, 4, /* ICR2A */ \ + { p ## _IRQ8, p ## _IRQ9, p ## _IRQ10, p ## _IRQ11, \ + p ## _IRQ12, p ## _IRQ13, p ## _IRQ14, p ## _IRQ15 } } + +#define INTC_IRQ_PINS_SENSE_16H(p, base) \ + { base + 0x08, 32, 4, /* ICR3A */ \ + { p ## _IRQ16, p ## _IRQ17, p ## _IRQ18, p ## _IRQ19, \ + p ## _IRQ20, p ## _IRQ21, p ## _IRQ22, p ## _IRQ23 } }, \ + { base + 0x0c, 32, 4, /* ICR4A */ \ + { p ## _IRQ24, p ## _IRQ25, p ## _IRQ26, p ## _IRQ27, \ + p ## _IRQ28, p ## _IRQ29, p ## _IRQ30, p ## _IRQ31 } } + +#define INTC_IRQ_PINS_ACK_16L(p, base) \ + { base + 0x20, 0, 8, /* INTREQ00A */ \ + { p ## _IRQ0, p ## _IRQ1, p ## _IRQ2, p ## _IRQ3, \ + p ## _IRQ4, p ## _IRQ5, p ## _IRQ6, p ## _IRQ7 } }, \ + { base + 0x24, 0, 8, /* INTREQ10A */ \ + { p ## _IRQ8, p ## _IRQ9, p ## _IRQ10, p ## _IRQ11, \ + p ## _IRQ12, p ## _IRQ13, p ## _IRQ14, p ## _IRQ15 } } + +#define INTC_IRQ_PINS_ACK_16H(p, base) \ + { base + 0x28, 0, 8, /* INTREQ20A */ \ + { p ## _IRQ16, p ## _IRQ17, p ## _IRQ18, p ## _IRQ19, \ + p ## _IRQ20, p ## _IRQ21, p ## _IRQ22, p ## _IRQ23 } }, \ + { base + 0x2c, 0, 8, /* INTREQ30A */ \ + { p ## _IRQ24, p ## _IRQ25, p ## _IRQ26, p ## _IRQ27, \ + p ## _IRQ28, p ## _IRQ29, p ## _IRQ30, p ## _IRQ31 } } + +#define INTC_IRQ_PINS_16(p, base, vect, str) \ + \ +static struct resource p ## _resources[] __initdata = { \ + [0] = { \ + .start = base, \ + .end = base + 0x64, \ + .flags = IORESOURCE_MEM, \ + }, \ +}; \ + \ +enum { \ + p ## _UNUSED = 0, \ + INTC_IRQ_PINS_ENUM_16L(p), \ +}; \ + \ +static struct intc_vect p ## _vectors[] __initdata = { \ + INTC_IRQ_PINS_VECT_16L(p, vect), \ +}; \ + \ +static struct intc_mask_reg p ## _mask_registers[] __initdata = { \ + INTC_IRQ_PINS_MASK_16L(p, base), \ +}; \ + \ +static struct intc_prio_reg p ## _prio_registers[] __initdata = { \ + INTC_IRQ_PINS_PRIO_16L(p, base), \ +}; \ + \ +static struct intc_sense_reg p ## _sense_registers[] __initdata = { \ + INTC_IRQ_PINS_SENSE_16L(p, base), \ +}; \ + \ +static struct intc_mask_reg p ## _ack_registers[] __initdata = { \ + INTC_IRQ_PINS_ACK_16L(p, base), \ +}; \ + \ +static struct intc_desc p ## _desc __initdata = { \ + .name = str, \ + .resource = p ## _resources, \ + .num_resources = ARRAY_SIZE(p ## _resources), \ + .hw = INTC_HW_DESC(p ## _vectors, NULL, \ + p ## _mask_registers, p ## _prio_registers, \ + p ## _sense_registers, p ## _ack_registers) \ +} + +#define INTC_IRQ_PINS_16H(p, base, vect, str) \ + \ +static struct resource p ## _resources[] __initdata = { \ + [0] = { \ + .start = base, \ + .end = base + 0x64, \ + .flags = IORESOURCE_MEM, \ + }, \ +}; \ + \ +enum { \ + p ## _UNUSED = 0, \ + INTC_IRQ_PINS_ENUM_16H(p), \ +}; \ + \ +static struct intc_vect p ## _vectors[] __initdata = { \ + INTC_IRQ_PINS_VECT_16H(p, vect), \ +}; \ + \ +static struct intc_mask_reg p ## _mask_registers[] __initdata = { \ + INTC_IRQ_PINS_MASK_16H(p, base), \ +}; \ + \ +static struct intc_prio_reg p ## _prio_registers[] __initdata = { \ + INTC_IRQ_PINS_PRIO_16H(p, base), \ +}; \ + \ +static struct intc_sense_reg p ## _sense_registers[] __initdata = { \ + INTC_IRQ_PINS_SENSE_16H(p, base), \ +}; \ + \ +static struct intc_mask_reg p ## _ack_registers[] __initdata = { \ + INTC_IRQ_PINS_ACK_16H(p, base), \ +}; \ + \ +static struct intc_desc p ## _desc __initdata = { \ + .name = str, \ + .resource = p ## _resources, \ + .num_resources = ARRAY_SIZE(p ## _resources), \ + .hw = INTC_HW_DESC(p ## _vectors, NULL, \ + p ## _mask_registers, p ## _prio_registers, \ + p ## _sense_registers, p ## _ack_registers) \ +} + +#define INTC_IRQ_PINS_32(p, base, vect, str) \ + \ +static struct resource p ## _resources[] __initdata = { \ + [0] = { \ + .start = base, \ + .end = base + 0x6c, \ + .flags = IORESOURCE_MEM, \ + }, \ +}; \ + \ +enum { \ + p ## _UNUSED = 0, \ + INTC_IRQ_PINS_ENUM_16L(p), \ + INTC_IRQ_PINS_ENUM_16H(p), \ +}; \ + \ +static struct intc_vect p ## _vectors[] __initdata = { \ + INTC_IRQ_PINS_VECT_16L(p, vect), \ + INTC_IRQ_PINS_VECT_16H(p, vect), \ +}; \ + \ +static struct intc_mask_reg p ## _mask_registers[] __initdata = { \ + INTC_IRQ_PINS_MASK_16L(p, base), \ + INTC_IRQ_PINS_MASK_16H(p, base), \ +}; \ + \ +static struct intc_prio_reg p ## _prio_registers[] __initdata = { \ + INTC_IRQ_PINS_PRIO_16L(p, base), \ + INTC_IRQ_PINS_PRIO_16H(p, base), \ +}; \ + \ +static struct intc_sense_reg p ## _sense_registers[] __initdata = { \ + INTC_IRQ_PINS_SENSE_16L(p, base), \ + INTC_IRQ_PINS_SENSE_16H(p, base), \ +}; \ + \ +static struct intc_mask_reg p ## _ack_registers[] __initdata = { \ + INTC_IRQ_PINS_ACK_16L(p, base), \ + INTC_IRQ_PINS_ACK_16H(p, base), \ +}; \ + \ +static struct intc_desc p ## _desc __initdata = { \ + .name = str, \ + .resource = p ## _resources, \ + .num_resources = ARRAY_SIZE(p ## _resources), \ + .hw = INTC_HW_DESC(p ## _vectors, NULL, \ + p ## _mask_registers, p ## _prio_registers, \ + p ## _sense_registers, p ## _ack_registers) \ +} + +#define INTC_PINT_E_EMPTY +#define INTC_PINT_E_NONE 0, 0, 0, 0, 0, 0, 0, 0, +#define INTC_PINT_E(p) \ + PINT ## p ## 0, PINT ## p ## 1, PINT ## p ## 2, PINT ## p ## 3, \ + PINT ## p ## 4, PINT ## p ## 5, PINT ## p ## 6, PINT ## p ## 7, + +#define INTC_PINT_V_NONE +#define INTC_PINT_V(p, vect) \ + vect(PINT ## p ## 0, 0), vect(PINT ## p ## 1, 1), \ + vect(PINT ## p ## 2, 2), vect(PINT ## p ## 3, 3), \ + vect(PINT ## p ## 4, 4), vect(PINT ## p ## 5, 5), \ + vect(PINT ## p ## 6, 6), vect(PINT ## p ## 7, 7), + +#define INTC_PINT(p, mask_reg, sense_base, str, \ + enums_1, enums_2, enums_3, enums_4, \ + vect_1, vect_2, vect_3, vect_4, \ + mask_a, mask_b, mask_c, mask_d, \ + sense_a, sense_b, sense_c, sense_d) \ + \ +enum { \ + PINT ## p ## _UNUSED = 0, \ + enums_1 enums_2 enums_3 enums_4 \ +}; \ + \ +static struct intc_vect p ## _vectors[] __initdata = { \ + vect_1 vect_2 vect_3 vect_4 \ +}; \ + \ +static struct intc_mask_reg p ## _mask_registers[] __initdata = { \ + { mask_reg, 0, 32, /* PINTER */ \ + { mask_a mask_b mask_c mask_d } } \ +}; \ + \ +static struct intc_sense_reg p ## _sense_registers[] __initdata = { \ + { sense_base + 0x00, 16, 2, /* PINTCR */ \ + { sense_a } }, \ + { sense_base + 0x04, 16, 2, /* PINTCR */ \ + { sense_b } }, \ + { sense_base + 0x08, 16, 2, /* PINTCR */ \ + { sense_c } }, \ + { sense_base + 0x0c, 16, 2, /* PINTCR */ \ + { sense_d } }, \ +}; \ + \ +static struct intc_desc p ## _desc __initdata = { \ + .name = str, \ + .hw = INTC_HW_DESC(p ## _vectors, NULL, \ + p ## _mask_registers, NULL, \ + p ## _sense_registers, NULL), \ +} + +/* INTCS */ +#define INTCS_VECT_BASE 0x3400 +#define INTCS_VECT(n, vect) INTC_VECT((n), INTCS_VECT_BASE + (vect)) +#define intcs_evt2irq(evt) evt2irq(INTCS_VECT_BASE + (evt)) + +#endif /* __ASM_MACH_INTC_H */ diff --git a/arch/arm/mach-shmobile/irqs.h b/arch/arm/mach-shmobile/irqs.h new file mode 100644 index 000000000..3070f6d88 --- /dev/null +++ b/arch/arm/mach-shmobile/irqs.h @@ -0,0 +1,15 @@ +#ifndef __SHMOBILE_IRQS_H +#define __SHMOBILE_IRQS_H + +#include "include/mach/irqs.h" + +/* GIC */ +#define gic_spi(nr) ((nr) + 32) +#define gic_iid(nr) (nr) /* ICCIAR / interrupt ID */ + +/* GPIO IRQ */ +#define _GPIO_IRQ_BASE 2500 +#define GPIO_IRQ_BASE(x) (_GPIO_IRQ_BASE + (32 * x)) +#define GPIO_IRQ(x, y) (_GPIO_IRQ_BASE + (32 * x) + y) + +#endif /* __SHMOBILE_IRQS_H */ diff --git a/arch/arm/mach-shmobile/platsmp-apmu.c b/arch/arm/mach-shmobile/platsmp-apmu.c new file mode 100644 index 000000000..f483b560b --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp-apmu.c @@ -0,0 +1,238 @@ +/* + * SMP support for SoCs with APMU + * + * Copyright (C) 2014 Renesas Electronics Corporation + * 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 version 2 as + * published by the Free Software Foundation. + */ +#include <linux/cpu_pm.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/of_address.h> +#include <linux/smp.h> +#include <linux/suspend.h> +#include <linux/threads.h> +#include <asm/cacheflush.h> +#include <asm/cp15.h> +#include <asm/proc-fns.h> +#include <asm/smp_plat.h> +#include <asm/suspend.h> +#include "common.h" +#include "platsmp-apmu.h" + +static struct { + void __iomem *iomem; + int bit; +} apmu_cpus[NR_CPUS]; + +#define WUPCR_OFFS 0x10 +#define PSTR_OFFS 0x40 +#define CPUNCR_OFFS(n) (0x100 + (0x10 * (n))) + +static int __maybe_unused apmu_power_on(void __iomem *p, int bit) +{ + /* request power on */ + writel_relaxed(BIT(bit), p + WUPCR_OFFS); + + /* wait for APMU to finish */ + while (readl_relaxed(p + WUPCR_OFFS) != 0) + ; + + return 0; +} + +static int apmu_power_off(void __iomem *p, int bit) +{ + /* request Core Standby for next WFI */ + writel_relaxed(3, p + CPUNCR_OFFS(bit)); + return 0; +} + +static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit) +{ + int k; + + for (k = 0; k < 1000; k++) { + if (((readl_relaxed(p + PSTR_OFFS) >> (bit * 4)) & 0x03) == 3) + return 1; + + mdelay(1); + } + + return 0; +} + +static int apmu_wrap(int cpu, int (*fn)(void __iomem *p, int cpu)) +{ + void __iomem *p = apmu_cpus[cpu].iomem; + + return p ? fn(p, apmu_cpus[cpu].bit) : -EINVAL; +} + +static void apmu_init_cpu(struct resource *res, int cpu, int bit) +{ + if ((cpu >= ARRAY_SIZE(apmu_cpus)) || apmu_cpus[cpu].iomem) + return; + + apmu_cpus[cpu].iomem = ioremap_nocache(res->start, resource_size(res)); + apmu_cpus[cpu].bit = bit; + + pr_debug("apmu ioremap %d %d %pr\n", cpu, bit, res); +} + +static void apmu_parse_cfg(void (*fn)(struct resource *res, int cpu, int bit), + struct rcar_apmu_config *apmu_config, int num) +{ + u32 id; + int k; + int bit, index; + bool is_allowed; + + for (k = 0; k < num; k++) { + /* only enable the cluster that includes the boot CPU */ + is_allowed = false; + for (bit = 0; bit < ARRAY_SIZE(apmu_config[k].cpus); bit++) { + id = apmu_config[k].cpus[bit]; + if (id >= 0) { + if (id == cpu_logical_map(0)) + is_allowed = true; + } + } + if (!is_allowed) + continue; + + for (bit = 0; bit < ARRAY_SIZE(apmu_config[k].cpus); bit++) { + id = apmu_config[k].cpus[bit]; + if (id >= 0) { + index = get_logical_index(id); + if (index >= 0) + fn(&apmu_config[k].iomem, index, bit); + } + } + } +} + +void __init shmobile_smp_apmu_prepare_cpus(unsigned int max_cpus, + struct rcar_apmu_config *apmu_config, + int num) +{ + /* install boot code shared by all CPUs */ + shmobile_boot_fn = virt_to_phys(shmobile_smp_boot); + shmobile_boot_arg = MPIDR_HWID_BITMASK; + + /* perform per-cpu setup */ + apmu_parse_cfg(apmu_init_cpu, apmu_config, num); +} + +#ifdef CONFIG_SMP +int shmobile_smp_apmu_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + /* For this particular CPU register boot vector */ + shmobile_smp_hook(cpu, virt_to_phys(shmobile_invalidate_start), 0); + + return apmu_wrap(cpu, apmu_power_on); +} +#endif + +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND) +/* nicked from arch/arm/mach-exynos/hotplug.c */ +static inline void cpu_enter_lowpower_a15(void) +{ + unsigned int v; + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "Ir" (CR_C) + : "cc"); + + flush_cache_louis(); + + asm volatile( + /* + * Turn off coherency + */ + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (0x40) + : "cc"); + + isb(); + dsb(); +} + +void shmobile_smp_apmu_cpu_shutdown(unsigned int cpu) +{ + + /* Select next sleep mode using the APMU */ + apmu_wrap(cpu, apmu_power_off); + + /* Do ARM specific CPU shutdown */ + cpu_enter_lowpower_a15(); +} + +static inline void cpu_leave_lowpower(void) +{ + unsigned int v; + + asm volatile("mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (0x40) + : "cc"); +} +#endif + +#if defined(CONFIG_HOTPLUG_CPU) +void shmobile_smp_apmu_cpu_die(unsigned int cpu) +{ + /* For this particular CPU deregister boot vector */ + shmobile_smp_hook(cpu, 0, 0); + + /* Shutdown CPU core */ + shmobile_smp_apmu_cpu_shutdown(cpu); + + /* jump to shared mach-shmobile sleep / reset code */ + shmobile_smp_sleep(); +} + +int shmobile_smp_apmu_cpu_kill(unsigned int cpu) +{ + return apmu_wrap(cpu, apmu_power_off_poll); +} +#endif + +#if defined(CONFIG_SUSPEND) +static int shmobile_smp_apmu_do_suspend(unsigned long cpu) +{ + shmobile_smp_hook(cpu, virt_to_phys(cpu_resume), 0); + shmobile_smp_apmu_cpu_shutdown(cpu); + cpu_do_idle(); /* WFI selects Core Standby */ + return 1; +} + +static int shmobile_smp_apmu_enter_suspend(suspend_state_t state) +{ + cpu_suspend(smp_processor_id(), shmobile_smp_apmu_do_suspend); + cpu_leave_lowpower(); + return 0; +} + +void __init shmobile_smp_apmu_suspend_init(void) +{ + shmobile_suspend_ops.enter = shmobile_smp_apmu_enter_suspend; +} +#endif diff --git a/arch/arm/mach-shmobile/platsmp-apmu.h b/arch/arm/mach-shmobile/platsmp-apmu.h new file mode 100644 index 000000000..76512c9a2 --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp-apmu.h @@ -0,0 +1,32 @@ +/* + * rmobile apmu definition + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * 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. + */ + +#ifndef PLATSMP_APMU_H +#define PLATSMP_APMU_H + +struct rcar_apmu_config { + struct resource iomem; + int cpus[4]; +}; + +extern void shmobile_smp_apmu_prepare_cpus(unsigned int max_cpus, + struct rcar_apmu_config *apmu_config, + int num); +extern int shmobile_smp_apmu_boot_secondary(unsigned int cpu, + struct task_struct *idle); +extern void shmobile_smp_apmu_cpu_die(unsigned int cpu); +extern int shmobile_smp_apmu_cpu_kill(unsigned int cpu); + +#endif /* PLATSMP_APMU_H */ diff --git a/arch/arm/mach-shmobile/platsmp-scu.c b/arch/arm/mach-shmobile/platsmp-scu.c new file mode 100644 index 000000000..64663110a --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp-scu.c @@ -0,0 +1,97 @@ +/* + * SMP support for SoCs with SCU covered by mach-shmobile + * + * 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 version 2 as + * published by the Free Software Foundation. + */ +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/smp.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include "common.h" + +static int shmobile_smp_scu_notifier_call(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (long)hcpu; + + switch (action) { + case CPU_UP_PREPARE: + /* For this particular CPU register SCU SMP boot vector */ + shmobile_smp_hook(cpu, virt_to_phys(shmobile_boot_scu), + (unsigned long)shmobile_scu_base); + break; + }; + + return NOTIFY_OK; +} + +static struct notifier_block shmobile_smp_scu_notifier = { + .notifier_call = shmobile_smp_scu_notifier_call, +}; + +void __init shmobile_smp_scu_prepare_cpus(unsigned int max_cpus) +{ + /* install boot code shared by all CPUs */ + shmobile_boot_fn = virt_to_phys(shmobile_smp_boot); + shmobile_boot_arg = MPIDR_HWID_BITMASK; + + /* enable SCU and cache coherency on booting CPU */ + scu_enable(shmobile_scu_base); + scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL); + + /* Use CPU notifier for reset vector control */ + register_cpu_notifier(&shmobile_smp_scu_notifier); +} + +#ifdef CONFIG_HOTPLUG_CPU +void shmobile_smp_scu_cpu_die(unsigned int cpu) +{ + /* For this particular CPU deregister boot vector */ + shmobile_smp_hook(cpu, 0, 0); + + dsb(); + flush_cache_all(); + + /* disable cache coherency */ + scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF); + + /* jump to shared mach-shmobile sleep / reset code */ + shmobile_smp_sleep(); +} + +static int shmobile_smp_scu_psr_core_disabled(int cpu) +{ + unsigned long mask = SCU_PM_POWEROFF << (cpu * 8); + + if ((__raw_readl(shmobile_scu_base + 8) & mask) == mask) + return 1; + + return 0; +} + +int shmobile_smp_scu_cpu_kill(unsigned int cpu) +{ + int k; + + /* this function is running on another CPU than the offline target, + * here we need wait for shutdown code in platform_cpu_die() to + * finish before asking SoC-specific code to power off the CPU core. + */ + for (k = 0; k < 1000; k++) { + if (shmobile_smp_scu_psr_core_disabled(cpu)) + return 1; + + mdelay(1); + } + + return 0; +} +#endif diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c new file mode 100644 index 000000000..3923e09e9 --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp.c @@ -0,0 +1,38 @@ +/* + * SMP support for R-Mobile / SH-Mobile + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2011 Paul Mundt + * + * Based on vexpress, Copyright (C) 2002 ARM Ltd, All Rights Reserved + * + * 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/init.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include "common.h" + +extern unsigned long shmobile_smp_fn[]; +extern unsigned long shmobile_smp_arg[]; +extern unsigned long shmobile_smp_mpidr[]; + +void shmobile_smp_hook(unsigned int cpu, unsigned long fn, unsigned long arg) +{ + shmobile_smp_fn[cpu] = 0; + flush_cache_all(); + + shmobile_smp_mpidr[cpu] = cpu_logical_map(cpu); + shmobile_smp_fn[cpu] = fn; + shmobile_smp_arg[cpu] = arg; + flush_cache_all(); +} + +#ifdef CONFIG_HOTPLUG_CPU +int shmobile_smp_cpu_disable(unsigned int cpu) +{ + return 0; /* Hotplug of any CPU is supported */ +} +#endif diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c new file mode 100644 index 000000000..34608fcf0 --- /dev/null +++ b/arch/arm/mach-shmobile/pm-r8a7740.c @@ -0,0 +1,129 @@ +/* + * r8a7740 power management support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/console.h> +#include <linux/io.h> +#include <linux/suspend.h> + +#include "common.h" +#include "pm-rmobile.h" + +#define SYSC_BASE IOMEM(0xe6180000) + +#if defined(CONFIG_PM) && !defined(CONFIG_ARCH_MULTIPLATFORM) +static int r8a7740_pd_a3sm_suspend(void) +{ + /* + * The A3SM domain contains the CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + return -EBUSY; +} + +static int r8a7740_pd_a3sp_suspend(void) +{ + /* + * Serial consoles make use of SCIF hardware located in A3SP, + * keep such power domain on if "no_console_suspend" is set. + */ + return console_suspend_enabled ? 0 : -EBUSY; +} + +static int r8a7740_pd_d4_suspend(void) +{ + /* + * The D4 domain contains the Coresight-ETM hardware block and + * therefore it should only be turned off if the debug module is + * not in use. + */ + return -EBUSY; +} + +static struct rmobile_pm_domain r8a7740_pm_domains[] = { + { + .genpd.name = "A4LC", + .base = SYSC_BASE, + .bit_shift = 1, + }, { + .genpd.name = "A4MP", + .base = SYSC_BASE, + .bit_shift = 2, + }, { + .genpd.name = "D4", + .base = SYSC_BASE, + .bit_shift = 3, + .gov = &pm_domain_always_on_gov, + .suspend = r8a7740_pd_d4_suspend, + }, { + .genpd.name = "A4R", + .base = SYSC_BASE, + .bit_shift = 5, + }, { + .genpd.name = "A3RV", + .base = SYSC_BASE, + .bit_shift = 6, + }, { + .genpd.name = "A4S", + .base = SYSC_BASE, + .bit_shift = 10, + .no_debug = true, + }, { + .genpd.name = "A3SP", + .base = SYSC_BASE, + .bit_shift = 11, + .gov = &pm_domain_always_on_gov, + .no_debug = true, + .suspend = r8a7740_pd_a3sp_suspend, + }, { + .genpd.name = "A3SM", + .base = SYSC_BASE, + .bit_shift = 12, + .gov = &pm_domain_always_on_gov, + .suspend = r8a7740_pd_a3sm_suspend, + }, { + .genpd.name = "A3SG", + .base = SYSC_BASE, + .bit_shift = 13, + }, { + .genpd.name = "A4SU", + .base = SYSC_BASE, + .bit_shift = 20, + }, +}; + +void __init r8a7740_init_pm_domains(void) +{ + rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains)); + pm_genpd_add_subdomain_names("A4R", "A3RV"); + pm_genpd_add_subdomain_names("A4S", "A3SP"); + pm_genpd_add_subdomain_names("A4S", "A3SM"); + pm_genpd_add_subdomain_names("A4S", "A3SG"); +} +#endif /* CONFIG_PM && !CONFIG_ARCH_MULTIPLATFORM */ + +#ifdef CONFIG_SUSPEND +static int r8a7740_enter_suspend(suspend_state_t suspend_state) +{ + cpu_do_idle(); + return 0; +} + +static void r8a7740_suspend_init(void) +{ + shmobile_suspend_ops.enter = r8a7740_enter_suspend; +} +#else +static void r8a7740_suspend_init(void) {} +#endif + +void __init r8a7740_pm_init(void) +{ + r8a7740_suspend_init(); +} diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c new file mode 100644 index 000000000..44a74c4c5 --- /dev/null +++ b/arch/arm/mach-shmobile/pm-r8a7779.c @@ -0,0 +1,143 @@ +/* + * r8a7779 Power management support + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/err.h> +#include <linux/pm_clock.h> +#include <linux/pm_domain.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/console.h> + +#include <asm/io.h> + +#include "common.h" +#include "pm-rcar.h" +#include "r8a7779.h" + +/* SYSC */ +#define SYSCIER 0x0c +#define SYSCIMR 0x10 + +struct r8a7779_pm_domain { + struct generic_pm_domain genpd; + struct rcar_sysc_ch ch; +}; + +static inline struct rcar_sysc_ch *to_r8a7779_ch(struct generic_pm_domain *d) +{ + return &container_of(d, struct r8a7779_pm_domain, genpd)->ch; +} + +#if defined(CONFIG_PM) || defined(CONFIG_SMP) + +static void __init r8a7779_sysc_init(void) +{ + void __iomem *base = rcar_sysc_init(0xffd85000); + + /* enable all interrupt sources, but do not use interrupt handler */ + iowrite32(0x0131000e, base + SYSCIER); + iowrite32(0, base + SYSCIMR); +} + +#else /* CONFIG_PM || CONFIG_SMP */ + +static inline void r8a7779_sysc_init(void) {} + +#endif /* CONFIG_PM || CONFIG_SMP */ + +#ifdef CONFIG_PM + +static int pd_power_down(struct generic_pm_domain *genpd) +{ + return rcar_sysc_power_down(to_r8a7779_ch(genpd)); +} + +static int pd_power_up(struct generic_pm_domain *genpd) +{ + return rcar_sysc_power_up(to_r8a7779_ch(genpd)); +} + +static bool pd_is_off(struct generic_pm_domain *genpd) +{ + return rcar_sysc_power_is_off(to_r8a7779_ch(genpd)); +} + +static bool pd_active_wakeup(struct device *dev) +{ + return true; +} + +static void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd) +{ + struct generic_pm_domain *genpd = &r8a7779_pd->genpd; + + genpd->flags = GENPD_FLAG_PM_CLK; + pm_genpd_init(genpd, NULL, false); + genpd->dev_ops.active_wakeup = pd_active_wakeup; + genpd->power_off = pd_power_down; + genpd->power_on = pd_power_up; + + if (pd_is_off(&r8a7779_pd->genpd)) + pd_power_up(&r8a7779_pd->genpd); +} + +static struct r8a7779_pm_domain r8a7779_pm_domains[] = { + { + .genpd.name = "SH4A", + .ch = { + .chan_offs = 0x80, /* PWRSR1 .. PWRER1 */ + .isr_bit = 16, /* SH4A */ + }, + }, + { + .genpd.name = "SGX", + .ch = { + .chan_offs = 0xc0, /* PWRSR2 .. PWRER2 */ + .isr_bit = 20, /* SGX */ + }, + }, + { + .genpd.name = "VDP1", + .ch = { + .chan_offs = 0x100, /* PWRSR3 .. PWRER3 */ + .isr_bit = 21, /* VDP */ + }, + }, + { + .genpd.name = "IMPX3", + .ch = { + .chan_offs = 0x140, /* PWRSR4 .. PWRER4 */ + .isr_bit = 24, /* IMP */ + }, + }, +}; + +void __init r8a7779_init_pm_domains(void) +{ + int j; + + for (j = 0; j < ARRAY_SIZE(r8a7779_pm_domains); j++) + r8a7779_init_pm_domain(&r8a7779_pm_domains[j]); +} + +#endif /* CONFIG_PM */ + +void __init r8a7779_pm_init(void) +{ + static int once; + + if (!once++) + r8a7779_sysc_init(); +} diff --git a/arch/arm/mach-shmobile/pm-rcar-gen2.c b/arch/arm/mach-shmobile/pm-rcar-gen2.c new file mode 100644 index 000000000..6815781ad --- /dev/null +++ b/arch/arm/mach-shmobile/pm-rcar-gen2.c @@ -0,0 +1,115 @@ +/* + * R-Car Generation 2 Power management support + * + * Copyright (C) 2013 - 2015 Renesas Electronics Corporation + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/smp.h> +#include <asm/io.h> +#include "common.h" +#include "pm-rcar.h" +#include "rcar-gen2.h" + +/* RST */ +#define RST 0xe6160000 +#define CA15BAR 0x0020 +#define CA7BAR 0x0030 +#define CA15RESCNT 0x0040 +#define CA7RESCNT 0x0044 + +/* On-chip RAM */ +#define MERAM 0xe8080000 +#define RAM 0xe6300000 + +/* SYSC */ +#define SYSCIER 0x0c +#define SYSCIMR 0x10 + +#if defined(CONFIG_SMP) + +static void __init rcar_gen2_sysc_init(u32 syscier) +{ + void __iomem *base = rcar_sysc_init(0xe6180000); + + /* enable all interrupt sources, but do not use interrupt handler */ + iowrite32(syscier, base + SYSCIER); + iowrite32(0, base + SYSCIMR); +} + +#else /* CONFIG_SMP */ + +static inline void rcar_gen2_sysc_init(u32 syscier) {} + +#endif /* CONFIG_SMP */ + +void __init rcar_gen2_pm_init(void) +{ + void __iomem *p; + u32 bar; + static int once; + struct device_node *np, *cpus; + bool has_a7 = false; + bool has_a15 = false; + phys_addr_t boot_vector_addr = 0; + u32 syscier = 0; + + if (once++) + return; + + cpus = of_find_node_by_path("/cpus"); + if (!cpus) + return; + + for_each_child_of_node(cpus, np) { + if (of_device_is_compatible(np, "arm,cortex-a15")) + has_a15 = true; + else if (of_device_is_compatible(np, "arm,cortex-a7")) + has_a7 = true; + } + + if (of_machine_is_compatible("renesas,r8a7790")) { + boot_vector_addr = MERAM; + syscier = 0x013111ef; + + } else if (of_machine_is_compatible("renesas,r8a7791")) { + boot_vector_addr = RAM; + syscier = 0x00111003; + } + + /* RAM for jump stub, because BAR requires 256KB aligned address */ + p = ioremap_nocache(boot_vector_addr, shmobile_boot_size); + memcpy_toio(p, shmobile_boot_vector, shmobile_boot_size); + iounmap(p); + + /* setup reset vectors */ + p = ioremap_nocache(RST, 0x63); + bar = (boot_vector_addr >> 8) & 0xfffffc00; + if (has_a15) { + writel_relaxed(bar, p + CA15BAR); + writel_relaxed(bar | 0x10, p + CA15BAR); + + /* de-assert reset for CA15 CPUs */ + writel_relaxed((readl_relaxed(p + CA15RESCNT) & ~0x0f) | + 0xa5a50000, p + CA15RESCNT); + } + if (has_a7) { + writel_relaxed(bar, p + CA7BAR); + writel_relaxed(bar | 0x10, p + CA7BAR); + + /* de-assert reset for CA7 CPUs */ + writel_relaxed((readl_relaxed(p + CA7RESCNT) & ~0x0f) | + 0x5a5a0000, p + CA7RESCNT); + } + iounmap(p); + + rcar_gen2_sysc_init(syscier); + shmobile_smp_apmu_suspend_init(); +} diff --git a/arch/arm/mach-shmobile/pm-rcar.c b/arch/arm/mach-shmobile/pm-rcar.c new file mode 100644 index 000000000..00022ee56 --- /dev/null +++ b/arch/arm/mach-shmobile/pm-rcar.c @@ -0,0 +1,137 @@ +/* + * R-Car SYSC Power management support + * + * Copyright (C) 2014 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/mm.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include "pm-rcar.h" + +/* SYSC */ +#define SYSCSR 0x00 +#define SYSCISR 0x04 +#define SYSCISCR 0x08 + +#define PWRSR_OFFS 0x00 +#define PWROFFCR_OFFS 0x04 +#define PWRONCR_OFFS 0x0c +#define PWRER_OFFS 0x14 + +#define SYSCSR_RETRIES 100 +#define SYSCSR_DELAY_US 1 + +#define SYSCISR_RETRIES 1000 +#define SYSCISR_DELAY_US 1 + +static void __iomem *rcar_sysc_base; +static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ + +static int rcar_sysc_pwr_on_off(struct rcar_sysc_ch *sysc_ch, + int sr_bit, int reg_offs) +{ + int k; + + for (k = 0; k < SYSCSR_RETRIES; k++) { + if (ioread32(rcar_sysc_base + SYSCSR) & (1 << sr_bit)) + break; + udelay(SYSCSR_DELAY_US); + } + + if (k == SYSCSR_RETRIES) + return -EAGAIN; + + iowrite32(1 << sysc_ch->chan_bit, + rcar_sysc_base + sysc_ch->chan_offs + reg_offs); + + return 0; +} + +static int rcar_sysc_pwr_off(struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_pwr_on_off(sysc_ch, 0, PWROFFCR_OFFS); +} + +static int rcar_sysc_pwr_on(struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_pwr_on_off(sysc_ch, 1, PWRONCR_OFFS); +} + +static int rcar_sysc_update(struct rcar_sysc_ch *sysc_ch, + int (*on_off_fn)(struct rcar_sysc_ch *)) +{ + unsigned int isr_mask = 1 << sysc_ch->isr_bit; + unsigned int chan_mask = 1 << sysc_ch->chan_bit; + unsigned int status; + unsigned long flags; + int ret = 0; + int k; + + spin_lock_irqsave(&rcar_sysc_lock, flags); + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + do { + ret = on_off_fn(sysc_ch); + if (ret) + goto out; + + status = ioread32(rcar_sysc_base + + sysc_ch->chan_offs + PWRER_OFFS); + } while (status & chan_mask); + + for (k = 0; k < SYSCISR_RETRIES; k++) { + if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask) + break; + udelay(SYSCISR_DELAY_US); + } + + if (k == SYSCISR_RETRIES) + ret = -EIO; + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + out: + spin_unlock_irqrestore(&rcar_sysc_lock, flags); + + pr_debug("sysc power domain %d: %08x -> %d\n", + sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); + return ret; +} + +int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_off); +} + +int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_on); +} + +bool rcar_sysc_power_is_off(struct rcar_sysc_ch *sysc_ch) +{ + unsigned int st; + + st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); + if (st & (1 << sysc_ch->chan_bit)) + return true; + + return false; +} + +void __iomem *rcar_sysc_init(phys_addr_t base) +{ + rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); + if (!rcar_sysc_base) + panic("unable to ioremap R-Car SYSC hardware block\n"); + + return rcar_sysc_base; +} diff --git a/arch/arm/mach-shmobile/pm-rcar.h b/arch/arm/mach-shmobile/pm-rcar.h new file mode 100644 index 000000000..ef3a1ef62 --- /dev/null +++ b/arch/arm/mach-shmobile/pm-rcar.h @@ -0,0 +1,15 @@ +#ifndef PM_RCAR_H +#define PM_RCAR_H + +struct rcar_sysc_ch { + unsigned long chan_offs; + unsigned int chan_bit; + unsigned int isr_bit; +}; + +int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch); +int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch); +bool rcar_sysc_power_is_off(struct rcar_sysc_ch *sysc_ch); +void __iomem *rcar_sysc_init(phys_addr_t base); + +#endif /* PM_RCAR_H */ diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c new file mode 100644 index 000000000..95018209f --- /dev/null +++ b/arch/arm/mach-shmobile/pm-rmobile.c @@ -0,0 +1,434 @@ +/* + * rmobile power management support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Copyright (C) 2014 Glider bvba + * + * based on pm-sh7372.c + * Copyright (C) 2011 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_clock.h> +#include <linux/slab.h> + +#include <asm/io.h> + +#include "pm-rmobile.h" + +/* SYSC */ +#define SPDCR 0x08 /* SYS Power Down Control Register */ +#define SWUCR 0x14 /* SYS Wakeup Control Register */ +#define PSTR 0x80 /* Power Status Register */ + +#define PSTR_RETRIES 100 +#define PSTR_DELAY_US 10 + +static int rmobile_pd_power_down(struct generic_pm_domain *genpd) +{ + struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); + unsigned int mask; + + if (rmobile_pd->bit_shift == ~0) + return -EBUSY; + + mask = 1 << rmobile_pd->bit_shift; + if (rmobile_pd->suspend) { + int ret = rmobile_pd->suspend(); + + if (ret) + return ret; + } + + if (__raw_readl(rmobile_pd->base + PSTR) & mask) { + unsigned int retry_count; + __raw_writel(mask, rmobile_pd->base + SPDCR); + + for (retry_count = PSTR_RETRIES; retry_count; retry_count--) { + if (!(__raw_readl(rmobile_pd->base + SPDCR) & mask)) + break; + cpu_relax(); + } + } + + if (!rmobile_pd->no_debug) + pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", + genpd->name, mask, + __raw_readl(rmobile_pd->base + PSTR)); + + return 0; +} + +static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd, + bool do_resume) +{ + unsigned int mask; + unsigned int retry_count; + int ret = 0; + + if (rmobile_pd->bit_shift == ~0) + return 0; + + mask = 1 << rmobile_pd->bit_shift; + if (__raw_readl(rmobile_pd->base + PSTR) & mask) + goto out; + + __raw_writel(mask, rmobile_pd->base + SWUCR); + + for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) { + if (!(__raw_readl(rmobile_pd->base + SWUCR) & mask)) + break; + if (retry_count > PSTR_RETRIES) + udelay(PSTR_DELAY_US); + else + cpu_relax(); + } + if (!retry_count) + ret = -EIO; + + if (!rmobile_pd->no_debug) + pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", + rmobile_pd->genpd.name, mask, + __raw_readl(rmobile_pd->base + PSTR)); + +out: + if (ret == 0 && rmobile_pd->resume && do_resume) + rmobile_pd->resume(); + + return ret; +} + +static int rmobile_pd_power_up(struct generic_pm_domain *genpd) +{ + return __rmobile_pd_power_up(to_rmobile_pd(genpd), true); +} + +static bool rmobile_pd_active_wakeup(struct device *dev) +{ + return true; +} + +static int rmobile_pd_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + int error; + + error = pm_clk_create(dev); + if (error) { + dev_err(dev, "pm_clk_create failed %d\n", error); + return error; + } + + error = pm_clk_add(dev, NULL); + if (error) { + dev_err(dev, "pm_clk_add failed %d\n", error); + goto fail; + } + + return 0; + +fail: + pm_clk_destroy(dev); + return error; +} + +static void rmobile_pd_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + pm_clk_destroy(dev); +} + +static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) +{ + struct generic_pm_domain *genpd = &rmobile_pd->genpd; + struct dev_power_governor *gov = rmobile_pd->gov; + + genpd->flags = GENPD_FLAG_PM_CLK; + pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); + genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup; + genpd->power_off = rmobile_pd_power_down; + genpd->power_on = rmobile_pd_power_up; + genpd->attach_dev = rmobile_pd_attach_dev; + genpd->detach_dev = rmobile_pd_detach_dev; + __rmobile_pd_power_up(rmobile_pd, false); +} + +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + +void rmobile_init_domains(struct rmobile_pm_domain domains[], int num) +{ + int j; + + for (j = 0; j < num; j++) + rmobile_init_pm_domain(&domains[j]); +} + +void rmobile_add_device_to_domain_td(const char *domain_name, + struct platform_device *pdev, + struct gpd_timing_data *td) +{ + struct device *dev = &pdev->dev; + + __pm_genpd_name_add_device(domain_name, dev, td); +} + +void rmobile_add_devices_to_domains(struct pm_domain_device data[], + int size) +{ + struct gpd_timing_data latencies = { + .stop_latency_ns = DEFAULT_DEV_LATENCY_NS, + .start_latency_ns = DEFAULT_DEV_LATENCY_NS, + .save_state_latency_ns = DEFAULT_DEV_LATENCY_NS, + .restore_state_latency_ns = DEFAULT_DEV_LATENCY_NS, + }; + int j; + + for (j = 0; j < size; j++) + rmobile_add_device_to_domain_td(data[j].domain_name, + data[j].pdev, &latencies); +} + +#else /* !CONFIG_ARCH_SHMOBILE_LEGACY */ + +static int rmobile_pd_suspend_busy(void) +{ + /* + * This domain should not be turned off. + */ + return -EBUSY; +} + +static int rmobile_pd_suspend_console(void) +{ + /* + * Serial consoles make use of SCIF hardware located in this domain, + * hence keep the power domain on if "no_console_suspend" is set. + */ + return console_suspend_enabled ? 0 : -EBUSY; +} + +enum pd_types { + PD_NORMAL, + PD_CPU, + PD_CONSOLE, + PD_DEBUG, + PD_MEMCTL, +}; + +#define MAX_NUM_SPECIAL_PDS 16 + +static struct special_pd { + struct device_node *pd; + enum pd_types type; +} special_pds[MAX_NUM_SPECIAL_PDS] __initdata; + +static unsigned int num_special_pds __initdata; + +static const struct of_device_id special_ids[] __initconst = { + { .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG }, + { .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, }, + { .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, }, + { .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, }, + { /* sentinel */ }, +}; + +static void __init add_special_pd(struct device_node *np, enum pd_types type) +{ + unsigned int i; + struct device_node *pd; + + pd = of_parse_phandle(np, "power-domains", 0); + if (!pd) + return; + + for (i = 0; i < num_special_pds; i++) + if (pd == special_pds[i].pd && type == special_pds[i].type) { + of_node_put(pd); + return; + } + + if (num_special_pds == ARRAY_SIZE(special_pds)) { + pr_warn("Too many special PM domains\n"); + of_node_put(pd); + return; + } + + pr_debug("Special PM domain %s type %d for %s\n", pd->name, type, + np->full_name); + + special_pds[num_special_pds].pd = pd; + special_pds[num_special_pds].type = type; + num_special_pds++; +} + +static void __init get_special_pds(void) +{ + struct device_node *np; + const struct of_device_id *id; + + /* PM domains containing CPUs */ + for_each_node_by_type(np, "cpu") + add_special_pd(np, PD_CPU); + + /* PM domain containing console */ + if (of_stdout) + add_special_pd(of_stdout, PD_CONSOLE); + + /* PM domains containing other special devices */ + for_each_matching_node_and_match(np, special_ids, &id) + add_special_pd(np, (enum pd_types)id->data); +} + +static void __init put_special_pds(void) +{ + unsigned int i; + + for (i = 0; i < num_special_pds; i++) + of_node_put(special_pds[i].pd); +} + +static enum pd_types __init pd_type(const struct device_node *pd) +{ + unsigned int i; + + for (i = 0; i < num_special_pds; i++) + if (pd == special_pds[i].pd) + return special_pds[i].type; + + return PD_NORMAL; +} + +static void __init rmobile_setup_pm_domain(struct device_node *np, + struct rmobile_pm_domain *pd) +{ + const char *name = pd->genpd.name; + + switch (pd_type(np)) { + case PD_CPU: + /* + * This domain contains the CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + pr_debug("PM domain %s contains CPU\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_busy; + break; + + case PD_CONSOLE: + pr_debug("PM domain %s contains serial console\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_console; + break; + + case PD_DEBUG: + /* + * This domain contains the Coresight-ETM hardware block and + * therefore it should only be turned off if the debug module + * is not in use. + */ + pr_debug("PM domain %s contains Coresight-ETM\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_busy; + break; + + case PD_MEMCTL: + /* + * This domain contains a memory-controller and therefore it + * should only be turned off if memory is not in use. + */ + pr_debug("PM domain %s contains MEMCTL\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_busy; + break; + + case PD_NORMAL: + break; + } + + rmobile_init_pm_domain(pd); +} + +static int __init rmobile_add_pm_domains(void __iomem *base, + struct device_node *parent, + struct generic_pm_domain *genpd_parent) +{ + struct device_node *np; + + for_each_child_of_node(parent, np) { + struct rmobile_pm_domain *pd; + u32 idx = ~0; + + if (of_property_read_u32(np, "reg", &idx)) { + /* always-on domain */ + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->genpd.name = np->name; + pd->base = base; + pd->bit_shift = idx; + + rmobile_setup_pm_domain(np, pd); + if (genpd_parent) + pm_genpd_add_subdomain(genpd_parent, &pd->genpd); + of_genpd_add_provider_simple(np, &pd->genpd); + + rmobile_add_pm_domains(base, np, &pd->genpd); + } + return 0; +} + +static int __init rmobile_init_pm_domains(void) +{ + struct device_node *np, *pmd; + bool scanned = false; + void __iomem *base; + int ret = 0; + + for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") { + base = of_iomap(np, 0); + if (!base) { + pr_warn("%s cannot map reg 0\n", np->full_name); + continue; + } + + pmd = of_get_child_by_name(np, "pm-domains"); + if (!pmd) { + pr_warn("%s lacks pm-domains node\n", np->full_name); + continue; + } + + if (!scanned) { + /* Find PM domains containing special blocks */ + get_special_pds(); + scanned = true; + } + + ret = rmobile_add_pm_domains(base, pmd, NULL); + of_node_put(pmd); + if (ret) { + of_node_put(np); + break; + } + } + + put_special_pds(); + + return ret; +} + +core_initcall(rmobile_init_pm_domains); + +#endif /* !CONFIG_ARCH_SHMOBILE_LEGACY */ diff --git a/arch/arm/mach-shmobile/pm-rmobile.h b/arch/arm/mach-shmobile/pm-rmobile.h new file mode 100644 index 000000000..53219786f --- /dev/null +++ b/arch/arm/mach-shmobile/pm-rmobile.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Renesas Solutions Corp. + * + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#ifndef PM_RMOBILE_H +#define PM_RMOBILE_H + +#include <linux/pm_domain.h> + +#define DEFAULT_DEV_LATENCY_NS 250000 + +struct platform_device; + +struct rmobile_pm_domain { + struct generic_pm_domain genpd; + struct dev_power_governor *gov; + int (*suspend)(void); + void (*resume)(void); + void __iomem *base; + unsigned int bit_shift; + bool no_debug; +}; + +static inline +struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct rmobile_pm_domain, genpd); +} + +struct pm_domain_device { + const char *domain_name; + struct platform_device *pdev; +}; + +#if defined(CONFIG_PM_RMOBILE) && defined(CONFIG_ARCH_SHMOBILE_LEGACY) +extern void rmobile_init_domains(struct rmobile_pm_domain domains[], int num); +extern void rmobile_add_device_to_domain_td(const char *domain_name, + struct platform_device *pdev, + struct gpd_timing_data *td); + +static inline void rmobile_add_device_to_domain(const char *domain_name, + struct platform_device *pdev) +{ + rmobile_add_device_to_domain_td(domain_name, pdev, NULL); +} + +extern void rmobile_add_devices_to_domains(struct pm_domain_device data[], + int size); +#else + +#define rmobile_init_domains(domains, num) do { } while (0) +#define rmobile_add_device_to_domain_td(name, pdev, td) do { } while (0) +#define rmobile_add_device_to_domain(name, pdev) do { } while (0) + +static inline void rmobile_add_devices_to_domains(struct pm_domain_device d[], + int size) {} +#endif /* CONFIG_PM_RMOBILE */ + +#endif /* PM_RMOBILE_H */ diff --git a/arch/arm/mach-shmobile/pm-sh73a0.c b/arch/arm/mach-shmobile/pm-sh73a0.c new file mode 100644 index 000000000..a7e466817 --- /dev/null +++ b/arch/arm/mach-shmobile/pm-sh73a0.c @@ -0,0 +1,32 @@ +/* + * sh73a0 Power management support + * + * Copyright (C) 2012 Bastian Hecht <hechtb+renesas@gmail.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/suspend.h> +#include "common.h" + +#ifdef CONFIG_SUSPEND +static int sh73a0_enter_suspend(suspend_state_t suspend_state) +{ + cpu_do_idle(); + return 0; +} + +static void sh73a0_suspend_init(void) +{ + shmobile_suspend_ops.enter = sh73a0_enter_suspend; +} +#else +static void sh73a0_suspend_init(void) {} +#endif + +void __init sh73a0_pm_init(void) +{ + sh73a0_suspend_init(); +} diff --git a/arch/arm/mach-shmobile/r8a7740.h b/arch/arm/mach-shmobile/r8a7740.h new file mode 100644 index 000000000..ca7805ad7 --- /dev/null +++ b/arch/arm/mach-shmobile/r8a7740.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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. + */ + +#ifndef __ASM_R8A7740_H__ +#define __ASM_R8A7740_H__ + +/* + * MD_CKx pin + */ +#define MD_CK2 (1 << 2) +#define MD_CK1 (1 << 1) +#define MD_CK0 (1 << 0) + +/* DMA slave IDs */ +enum { + SHDMA_SLAVE_INVALID, + SHDMA_SLAVE_SDHI0_RX, + SHDMA_SLAVE_SDHI0_TX, + SHDMA_SLAVE_SDHI1_RX, + SHDMA_SLAVE_SDHI1_TX, + SHDMA_SLAVE_SDHI2_RX, + SHDMA_SLAVE_SDHI2_TX, + SHDMA_SLAVE_FSIA_RX, + SHDMA_SLAVE_FSIA_TX, + SHDMA_SLAVE_FSIB_TX, + SHDMA_SLAVE_USBHS_TX, + SHDMA_SLAVE_USBHS_RX, + SHDMA_SLAVE_MMCIF_TX, + SHDMA_SLAVE_MMCIF_RX, +}; + +extern void r8a7740_meram_workaround(void); +extern void r8a7740_init_irq_of(void); +extern void r8a7740_map_io(void); +extern void r8a7740_add_early_devices(void); +extern void r8a7740_add_standard_devices(void); +extern void r8a7740_clock_init(u8 md_ck); +extern void r8a7740_pinmux_init(void); +extern void r8a7740_pm_init(void); + +#if defined(CONFIG_PM) && !defined(CONFIG_ARCH_MULTIPLATFORM) +extern void __init r8a7740_init_pm_domains(void); +#else +static inline void r8a7740_init_pm_domains(void) {} +#endif /* CONFIG_PM && !CONFIG_ARCH_MULTIPLATFORM */ + +#endif /* __ASM_R8A7740_H__ */ diff --git a/arch/arm/mach-shmobile/r8a7778.h b/arch/arm/mach-shmobile/r8a7778.h new file mode 100644 index 000000000..f64fedb1f --- /dev/null +++ b/arch/arm/mach-shmobile/r8a7778.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * 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. + */ +#ifndef __ASM_R8A7778_H__ +#define __ASM_R8A7778_H__ + +#include <linux/sh_eth.h> + +/* HPB-DMA slave IDs */ +enum { + HPBDMA_SLAVE_DUMMY, + HPBDMA_SLAVE_SDHI0_TX, + HPBDMA_SLAVE_SDHI0_RX, + HPBDMA_SLAVE_SSI0_TX, + HPBDMA_SLAVE_SSI0_RX, + HPBDMA_SLAVE_SSI1_TX, + HPBDMA_SLAVE_SSI1_RX, + HPBDMA_SLAVE_SSI2_TX, + HPBDMA_SLAVE_SSI2_RX, + HPBDMA_SLAVE_SSI3_TX, + HPBDMA_SLAVE_SSI3_RX, + HPBDMA_SLAVE_SSI4_TX, + HPBDMA_SLAVE_SSI4_RX, + HPBDMA_SLAVE_SSI5_TX, + HPBDMA_SLAVE_SSI5_RX, + HPBDMA_SLAVE_SSI6_TX, + HPBDMA_SLAVE_SSI6_RX, + HPBDMA_SLAVE_SSI7_TX, + HPBDMA_SLAVE_SSI7_RX, + HPBDMA_SLAVE_SSI8_TX, + HPBDMA_SLAVE_SSI8_RX, + HPBDMA_SLAVE_HPBIF0_TX, + HPBDMA_SLAVE_HPBIF0_RX, + HPBDMA_SLAVE_HPBIF1_TX, + HPBDMA_SLAVE_HPBIF1_RX, + HPBDMA_SLAVE_HPBIF2_TX, + HPBDMA_SLAVE_HPBIF2_RX, + HPBDMA_SLAVE_HPBIF3_TX, + HPBDMA_SLAVE_HPBIF3_RX, + HPBDMA_SLAVE_HPBIF4_TX, + HPBDMA_SLAVE_HPBIF4_RX, + HPBDMA_SLAVE_HPBIF5_TX, + HPBDMA_SLAVE_HPBIF5_RX, + HPBDMA_SLAVE_HPBIF6_TX, + HPBDMA_SLAVE_HPBIF6_RX, + HPBDMA_SLAVE_HPBIF7_TX, + HPBDMA_SLAVE_HPBIF7_RX, + HPBDMA_SLAVE_HPBIF8_TX, + HPBDMA_SLAVE_HPBIF8_RX, + HPBDMA_SLAVE_USBFUNC_TX, + HPBDMA_SLAVE_USBFUNC_RX, +}; + +extern void r8a7778_add_standard_devices(void); +extern void r8a7778_add_standard_devices_dt(void); +extern void r8a7778_add_dt_devices(void); + +extern void r8a7778_init_late(void); +extern void r8a7778_init_irq_dt(void); +extern void r8a7778_clock_init(void); +extern void r8a7778_init_irq_extpin(int irlm); +extern void r8a7778_init_irq_extpin_dt(int irlm); +extern void r8a7778_pinmux_init(void); + +extern int r8a7778_usb_phy_power(bool enable); + +#endif /* __ASM_R8A7778_H__ */ diff --git a/arch/arm/mach-shmobile/r8a7779.h b/arch/arm/mach-shmobile/r8a7779.h new file mode 100644 index 000000000..19f97046d --- /dev/null +++ b/arch/arm/mach-shmobile/r8a7779.h @@ -0,0 +1,35 @@ +#ifndef __ASM_R8A7779_H__ +#define __ASM_R8A7779_H__ + +#include <linux/sh_clk.h> + +/* HPB-DMA slave IDs */ +enum { + HPBDMA_SLAVE_DUMMY, + HPBDMA_SLAVE_SDHI0_TX, + HPBDMA_SLAVE_SDHI0_RX, +}; + +extern void r8a7779_init_irq_extpin(int irlm); +extern void r8a7779_init_irq_extpin_dt(int irlm); +extern void r8a7779_init_irq_dt(void); +extern void r8a7779_map_io(void); +extern void r8a7779_earlytimer_init(void); +extern void r8a7779_add_early_devices(void); +extern void r8a7779_add_standard_devices(void); +extern void r8a7779_init_late(void); +extern u32 r8a7779_read_mode_pins(void); +extern void r8a7779_clock_init(void); +extern void r8a7779_pinmux_init(void); +extern void r8a7779_pm_init(void); +extern void r8a7779_register_twd(void); + +#ifdef CONFIG_PM +extern void __init r8a7779_init_pm_domains(void); +#else +static inline void r8a7779_init_pm_domains(void) {} +#endif /* CONFIG_PM */ + +extern struct smp_operations r8a7779_smp_ops; + +#endif /* __ASM_R8A7779_H__ */ diff --git a/arch/arm/mach-shmobile/r8a7790.h b/arch/arm/mach-shmobile/r8a7790.h new file mode 100644 index 000000000..1a46d0260 --- /dev/null +++ b/arch/arm/mach-shmobile/r8a7790.h @@ -0,0 +1,6 @@ +#ifndef __ASM_R8A7790_H__ +#define __ASM_R8A7790_H__ + +extern struct smp_operations r8a7790_smp_ops; + +#endif /* __ASM_R8A7790_H__ */ diff --git a/arch/arm/mach-shmobile/r8a7791.h b/arch/arm/mach-shmobile/r8a7791.h new file mode 100644 index 000000000..7ca0b7d0f --- /dev/null +++ b/arch/arm/mach-shmobile/r8a7791.h @@ -0,0 +1,6 @@ +#ifndef __ASM_R8A7791_H__ +#define __ASM_R8A7791_H__ + +extern struct smp_operations r8a7791_smp_ops; + +#endif /* __ASM_R8A7791_H__ */ diff --git a/arch/arm/mach-shmobile/rcar-gen2.h b/arch/arm/mach-shmobile/rcar-gen2.h new file mode 100644 index 000000000..8a66b4aae --- /dev/null +++ b/arch/arm/mach-shmobile/rcar-gen2.h @@ -0,0 +1,10 @@ +#ifndef __ASM_RCAR_GEN2_H__ +#define __ASM_RCAR_GEN2_H__ + +void rcar_gen2_timer_init(void); +#define MD(nr) BIT(nr) +u32 rcar_gen2_read_mode_pins(void); +void rcar_gen2_reserve(void); +void rcar_gen2_pm_init(void); + +#endif /* __ASM_RCAR_GEN2_H__ */ diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c new file mode 100644 index 000000000..384e6e934 --- /dev/null +++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c @@ -0,0 +1,147 @@ +/* + * R-Car Generation 2 da9063/da9210 regulator quirk + * + * The r8a7790/lager and r8a7791/koelsch development boards have da9063 and + * da9210 regulators. Both regulators have their interrupt request lines tied + * to the same interrupt pin (IRQ2) on the SoC. + * + * After cold boot or da9063-induced restart, both the da9063 and da9210 seem + * to assert their interrupt request lines. Hence as soon as one driver + * requests this irq, it gets stuck in an interrupt storm, as it only manages + * to deassert its own interrupt request line, and the other driver hasn't + * installed an interrupt handler yet. + * + * To handle this, install a quirk that masks the interrupts in both the + * da9063 and da9210. This quirk has to run after the i2c master driver has + * been initialized, but before the i2c slave drivers are initialized. + * + * Copyright (C) 2015 Glider bvba + * + * 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. + */ + +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/mfd/da9063/registers.h> + + +#define IRQC_BASE 0xe61c0000 +#define IRQC_MONITOR 0x104 /* IRQn Signal Level Monitor Register */ + +#define REGULATOR_IRQ_MASK BIT(2) /* IRQ2, active low */ + +static void __iomem *irqc; + +static const u8 da9063_mask_regs[] = { + DA9063_REG_IRQ_MASK_A, + DA9063_REG_IRQ_MASK_B, + DA9063_REG_IRQ_MASK_C, + DA9063_REG_IRQ_MASK_D, +}; + +/* DA9210 System Control and Event Registers */ +#define DA9210_REG_MASK_A 0x54 +#define DA9210_REG_MASK_B 0x55 + +static const u8 da9210_mask_regs[] = { + DA9210_REG_MASK_A, + DA9210_REG_MASK_B, +}; + +static void da9xxx_mask_irqs(struct i2c_client *client, const u8 regs[], + unsigned int nregs) +{ + unsigned int i; + + dev_info(&client->dev, "Masking %s interrupt sources\n", client->name); + + for (i = 0; i < nregs; i++) { + int error = i2c_smbus_write_byte_data(client, regs[i], ~0); + if (error) { + dev_err(&client->dev, "i2c error %d\n", error); + return; + } + } +} + +static int regulator_quirk_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct i2c_client *client; + u32 mon; + + mon = ioread32(irqc + IRQC_MONITOR); + dev_dbg(dev, "%s: %ld, IRQC_MONITOR = 0x%x\n", __func__, action, mon); + if (mon & REGULATOR_IRQ_MASK) + goto remove; + + if (action != BUS_NOTIFY_ADD_DEVICE || dev->type == &i2c_adapter_type) + return 0; + + client = to_i2c_client(dev); + dev_dbg(dev, "Detected %s\n", client->name); + + if ((client->addr == 0x58 && !strcmp(client->name, "da9063"))) + da9xxx_mask_irqs(client, da9063_mask_regs, + ARRAY_SIZE(da9063_mask_regs)); + else if (client->addr == 0x68 && !strcmp(client->name, "da9210")) + da9xxx_mask_irqs(client, da9210_mask_regs, + ARRAY_SIZE(da9210_mask_regs)); + + mon = ioread32(irqc + IRQC_MONITOR); + if (mon & REGULATOR_IRQ_MASK) + goto remove; + + return 0; + +remove: + dev_info(dev, "IRQ2 is not asserted, removing quirk\n"); + + bus_unregister_notifier(&i2c_bus_type, nb); + iounmap(irqc); + return 0; +} + +static struct notifier_block regulator_quirk_nb = { + .notifier_call = regulator_quirk_notify +}; + +static int __init rcar_gen2_regulator_quirk(void) +{ + u32 mon; + + if (!of_machine_is_compatible("renesas,koelsch") && + !of_machine_is_compatible("renesas,lager")) + return -ENODEV; + + irqc = ioremap(IRQC_BASE, PAGE_SIZE); + if (!irqc) + return -ENOMEM; + + mon = ioread32(irqc + IRQC_MONITOR); + if (mon & REGULATOR_IRQ_MASK) { + pr_debug("%s: IRQ2 is not asserted, not installing quirk\n", + __func__); + iounmap(irqc); + return 0; + } + + pr_info("IRQ2 is asserted, installing da9063/da9210 regulator quirk\n"); + + bus_register_notifier(&i2c_bus_type, ®ulator_quirk_nb); + return 0; +} + +arch_initcall(rcar_gen2_regulator_quirk); diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c new file mode 100644 index 000000000..37f7b15c0 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-emev2.c @@ -0,0 +1,53 @@ +/* + * Emma Mobile EV2 processor support + * + * Copyright (C) 2012 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include "common.h" + +static struct map_desc emev2_io_desc[] __initdata = { +#ifdef CONFIG_SMP + /* 2M mapping for SCU + L2 controller */ + { + .virtual = 0xf0000000, + .pfn = __phys_to_pfn(0x1e000000), + .length = SZ_2M, + .type = MT_DEVICE + }, +#endif +}; + +static void __init emev2_map_io(void) +{ + iotable_init(emev2_io_desc, ARRAY_SIZE(emev2_io_desc)); +} + +static const char *const emev2_boards_compat_dt[] __initconst = { + "renesas,emev2", + NULL, +}; + +extern struct smp_operations emev2_smp_ops; + +DT_MACHINE_START(EMEV2_DT, "Generic Emma Mobile EV2 (Flattened Device Tree)") + .smp = smp_ops(emev2_smp_ops), + .map_io = emev2_map_io, + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .dt_compat = emev2_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r7s72100.c b/arch/arm/mach-shmobile/setup-r7s72100.c new file mode 100644 index 000000000..171174777 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r7s72100.c @@ -0,0 +1,32 @@ +/* + * r7s72100 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. + */ + +#include <linux/kernel.h> + +#include <asm/mach/arch.h> + +#include "common.h" + +static const char *r7s72100_boards_compat_dt[] __initdata = { + "renesas,r7s72100", + NULL, +}; + +DT_MACHINE_START(R7S72100_DT, "Generic R7S72100 (Flattened Device Tree)") + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .dt_compat = r7s72100_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a73a4.c b/arch/arm/mach-shmobile/setup-r8a73a4.c new file mode 100644 index 000000000..446cee611 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a73a4.c @@ -0,0 +1,32 @@ +/* + * r8a73a4 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. + */ + +#include <linux/init.h> + +#include <asm/mach/arch.h> + +#include "common.h" + +static const char *r8a73a4_boards_compat_dt[] __initdata = { + "renesas,r8a73a4", + NULL, +}; + +DT_MACHINE_START(R8A73A4_DT, "Generic R8A73A4 (Flattened Device Tree)") + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .dt_compat = r8a73a4_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c new file mode 100644 index 000000000..9832e4839 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7740.c @@ -0,0 +1,859 @@ +/* + * R8A7740 processor support + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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. + */ +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irqchip.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/serial_sci.h> +#include <linux/sh_dma.h> +#include <linux/sh_timer.h> +#include <linux/platform_data/sh_ipmmu.h> + +#include <asm/mach-types.h> +#include <asm/mach/map.h> +#include <asm/mach/arch.h> +#include <asm/mach/time.h> +#include <asm/hardware/cache-l2x0.h> + +#include "common.h" +#include "dma-register.h" +#include "irqs.h" +#include "pm-rmobile.h" +#include "r8a7740.h" + +static struct map_desc r8a7740_io_desc[] __initdata = { + /* + * for CPGA/INTC/PFC + * 0xe6000000-0xefffffff -> 0xe6000000-0xefffffff + */ + { + .virtual = 0xe6000000, + .pfn = __phys_to_pfn(0xe6000000), + .length = 160 << 20, + .type = MT_DEVICE_NONSHARED + }, +#ifdef CONFIG_CACHE_L2X0 + /* + * for l2x0_init() + * 0xf0100000-0xf0101000 -> 0xf0002000-0xf0003000 + */ + { + .virtual = 0xf0002000, + .pfn = __phys_to_pfn(0xf0100000), + .length = PAGE_SIZE, + .type = MT_DEVICE_NONSHARED + }, +#endif +}; + +void __init r8a7740_map_io(void) +{ + debug_ll_io_init(); + iotable_init(r8a7740_io_desc, ARRAY_SIZE(r8a7740_io_desc)); +} + +/* PFC */ +static const struct resource pfc_resources[] = { + DEFINE_RES_MEM(0xe6050000, 0x8000), + DEFINE_RES_MEM(0xe605800c, 0x0020), +}; + +void __init r8a7740_pinmux_init(void) +{ + platform_device_register_simple("pfc-r8a7740", -1, pfc_resources, + ARRAY_SIZE(pfc_resources)); +} + +static struct renesas_intc_irqpin_config irqpin0_platform_data = { + .irq_base = irq_pin(0), /* IRQ0 -> IRQ7 */ +}; + +static struct resource irqpin0_resources[] = { + DEFINE_RES_MEM(0xe6900000, 4), /* ICR1A */ + DEFINE_RES_MEM(0xe6900010, 4), /* INTPRI00A */ + DEFINE_RES_MEM(0xe6900020, 1), /* INTREQ00A */ + DEFINE_RES_MEM(0xe6900040, 1), /* INTMSK00A */ + DEFINE_RES_MEM(0xe6900060, 1), /* INTMSKCLR00A */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ0 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ1 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ2 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ3 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ4 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ5 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ6 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ7 */ +}; + +static struct platform_device irqpin0_device = { + .name = "renesas_intc_irqpin", + .id = 0, + .resource = irqpin0_resources, + .num_resources = ARRAY_SIZE(irqpin0_resources), + .dev = { + .platform_data = &irqpin0_platform_data, + }, +}; + +static struct renesas_intc_irqpin_config irqpin1_platform_data = { + .irq_base = irq_pin(8), /* IRQ8 -> IRQ15 */ +}; + +static struct resource irqpin1_resources[] = { + DEFINE_RES_MEM(0xe6900004, 4), /* ICR2A */ + DEFINE_RES_MEM(0xe6900014, 4), /* INTPRI10A */ + DEFINE_RES_MEM(0xe6900024, 1), /* INTREQ10A */ + DEFINE_RES_MEM(0xe6900044, 1), /* INTMSK10A */ + DEFINE_RES_MEM(0xe6900064, 1), /* INTMSKCLR10A */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ8 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ9 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ10 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ11 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ12 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ13 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ14 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ15 */ +}; + +static struct platform_device irqpin1_device = { + .name = "renesas_intc_irqpin", + .id = 1, + .resource = irqpin1_resources, + .num_resources = ARRAY_SIZE(irqpin1_resources), + .dev = { + .platform_data = &irqpin1_platform_data, + }, +}; + +static struct renesas_intc_irqpin_config irqpin2_platform_data = { + .irq_base = irq_pin(16), /* IRQ16 -> IRQ23 */ +}; + +static struct resource irqpin2_resources[] = { + DEFINE_RES_MEM(0xe6900008, 4), /* ICR3A */ + DEFINE_RES_MEM(0xe6900018, 4), /* INTPRI30A */ + DEFINE_RES_MEM(0xe6900028, 1), /* INTREQ30A */ + DEFINE_RES_MEM(0xe6900048, 1), /* INTMSK30A */ + DEFINE_RES_MEM(0xe6900068, 1), /* INTMSKCLR30A */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ16 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ17 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ18 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ19 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ20 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ21 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ22 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ23 */ +}; + +static struct platform_device irqpin2_device = { + .name = "renesas_intc_irqpin", + .id = 2, + .resource = irqpin2_resources, + .num_resources = ARRAY_SIZE(irqpin2_resources), + .dev = { + .platform_data = &irqpin2_platform_data, + }, +}; + +static struct renesas_intc_irqpin_config irqpin3_platform_data = { + .irq_base = irq_pin(24), /* IRQ24 -> IRQ31 */ +}; + +static struct resource irqpin3_resources[] = { + DEFINE_RES_MEM(0xe690000c, 4), /* ICR3A */ + DEFINE_RES_MEM(0xe690001c, 4), /* INTPRI30A */ + DEFINE_RES_MEM(0xe690002c, 1), /* INTREQ30A */ + DEFINE_RES_MEM(0xe690004c, 1), /* INTMSK30A */ + DEFINE_RES_MEM(0xe690006c, 1), /* INTMSKCLR30A */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ24 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ25 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ26 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ27 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ28 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ29 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ30 */ + DEFINE_RES_IRQ(gic_spi(149)), /* IRQ31 */ +}; + +static struct platform_device irqpin3_device = { + .name = "renesas_intc_irqpin", + .id = 3, + .resource = irqpin3_resources, + .num_resources = ARRAY_SIZE(irqpin3_resources), + .dev = { + .platform_data = &irqpin3_platform_data, + }, +}; + +/* SCIF */ +#define R8A7740_SCIF(scif_type, index, baseaddr, irq) \ +static struct plat_sci_port scif##index##_platform_data = { \ + .type = scif_type, \ + .flags = UPF_BOOT_AUTOCONF, \ + .scscr = SCSCR_RE | SCSCR_TE, \ +}; \ + \ +static struct resource scif##index##_resources[] = { \ + DEFINE_RES_MEM(baseaddr, 0x100), \ + DEFINE_RES_IRQ(irq), \ +}; \ + \ +static struct platform_device scif##index##_device = { \ + .name = "sh-sci", \ + .id = index, \ + .resource = scif##index##_resources, \ + .num_resources = ARRAY_SIZE(scif##index##_resources), \ + .dev = { \ + .platform_data = &scif##index##_platform_data, \ + }, \ +} + +R8A7740_SCIF(PORT_SCIFA, 0, 0xe6c40000, gic_spi(100)); +R8A7740_SCIF(PORT_SCIFA, 1, 0xe6c50000, gic_spi(101)); +R8A7740_SCIF(PORT_SCIFA, 2, 0xe6c60000, gic_spi(102)); +R8A7740_SCIF(PORT_SCIFA, 3, 0xe6c70000, gic_spi(103)); +R8A7740_SCIF(PORT_SCIFA, 4, 0xe6c80000, gic_spi(104)); +R8A7740_SCIF(PORT_SCIFA, 5, 0xe6cb0000, gic_spi(105)); +R8A7740_SCIF(PORT_SCIFA, 6, 0xe6cc0000, gic_spi(106)); +R8A7740_SCIF(PORT_SCIFA, 7, 0xe6cd0000, gic_spi(107)); +R8A7740_SCIF(PORT_SCIFB, 8, 0xe6c30000, gic_spi(108)); + +/* CMT */ +static struct sh_timer_config cmt1_platform_data = { + .channels_mask = 0x3f, +}; + +static struct resource cmt1_resources[] = { + DEFINE_RES_MEM(0xe6138000, 0x170), + DEFINE_RES_IRQ(gic_spi(58)), +}; + +static struct platform_device cmt1_device = { + .name = "sh-cmt-48", + .id = 1, + .dev = { + .platform_data = &cmt1_platform_data, + }, + .resource = cmt1_resources, + .num_resources = ARRAY_SIZE(cmt1_resources), +}; + +/* TMU */ +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xfff80000, 0x2c), + DEFINE_RES_IRQ(gic_spi(198)), + DEFINE_RES_IRQ(gic_spi(199)), + DEFINE_RES_IRQ(gic_spi(200)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +/* IPMMUI (an IPMMU module for ICB/LMB) */ +static struct resource ipmmu_resources[] = { + [0] = { + .name = "IPMMUI", + .start = 0xfe951000, + .end = 0xfe9510ff, + .flags = IORESOURCE_MEM, + }, +}; + +static const char * const ipmmu_dev_names[] = { + "sh_mobile_lcdc_fb.0", + "sh_mobile_lcdc_fb.1", + "sh_mobile_ceu.0", +}; + +static struct shmobile_ipmmu_platform_data ipmmu_platform_data = { + .dev_names = ipmmu_dev_names, + .num_dev_names = ARRAY_SIZE(ipmmu_dev_names), +}; + +static struct platform_device ipmmu_device = { + .name = "ipmmu", + .id = -1, + .dev = { + .platform_data = &ipmmu_platform_data, + }, + .resource = ipmmu_resources, + .num_resources = ARRAY_SIZE(ipmmu_resources), +}; + +static struct platform_device *r8a7740_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &scif8_device, + &irqpin0_device, + &irqpin1_device, + &irqpin2_device, + &irqpin3_device, + &tmu0_device, + &ipmmu_device, + &cmt1_device, +}; + +/* DMA */ +static const struct sh_dmae_slave_config r8a7740_dmae_slaves[] = { + { + .slave_id = SHDMA_SLAVE_SDHI0_TX, + .addr = 0xe6850030, + .chcr = CHCR_TX(XMIT_SZ_16BIT), + .mid_rid = 0xc1, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_RX, + .addr = 0xe6850030, + .chcr = CHCR_RX(XMIT_SZ_16BIT), + .mid_rid = 0xc2, + }, { + .slave_id = SHDMA_SLAVE_SDHI1_TX, + .addr = 0xe6860030, + .chcr = CHCR_TX(XMIT_SZ_16BIT), + .mid_rid = 0xc9, + }, { + .slave_id = SHDMA_SLAVE_SDHI1_RX, + .addr = 0xe6860030, + .chcr = CHCR_RX(XMIT_SZ_16BIT), + .mid_rid = 0xca, + }, { + .slave_id = SHDMA_SLAVE_SDHI2_TX, + .addr = 0xe6870030, + .chcr = CHCR_TX(XMIT_SZ_16BIT), + .mid_rid = 0xcd, + }, { + .slave_id = SHDMA_SLAVE_SDHI2_RX, + .addr = 0xe6870030, + .chcr = CHCR_RX(XMIT_SZ_16BIT), + .mid_rid = 0xce, + }, { + .slave_id = SHDMA_SLAVE_FSIA_TX, + .addr = 0xfe1f0024, + .chcr = CHCR_TX(XMIT_SZ_32BIT), + .mid_rid = 0xb1, + }, { + .slave_id = SHDMA_SLAVE_FSIA_RX, + .addr = 0xfe1f0020, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0xb2, + }, { + .slave_id = SHDMA_SLAVE_FSIB_TX, + .addr = 0xfe1f0064, + .chcr = CHCR_TX(XMIT_SZ_32BIT), + .mid_rid = 0xb5, + }, { + .slave_id = SHDMA_SLAVE_MMCIF_TX, + .addr = 0xe6bd0034, + .chcr = CHCR_TX(XMIT_SZ_32BIT), + .mid_rid = 0xd1, + }, { + .slave_id = SHDMA_SLAVE_MMCIF_RX, + .addr = 0xe6bd0034, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0xd2, + }, +}; + +#define DMA_CHANNEL(a, b, c) \ +{ \ + .offset = a, \ + .dmars = b, \ + .dmars_bit = c, \ + .chclr_offset = (0x220 - 0x20) + a \ +} + +static const struct sh_dmae_channel r8a7740_dmae_channels[] = { + DMA_CHANNEL(0x00, 0, 0), + DMA_CHANNEL(0x10, 0, 8), + DMA_CHANNEL(0x20, 4, 0), + DMA_CHANNEL(0x30, 4, 8), + DMA_CHANNEL(0x50, 8, 0), + DMA_CHANNEL(0x60, 8, 8), +}; + +static struct sh_dmae_pdata dma_platform_data = { + .slave = r8a7740_dmae_slaves, + .slave_num = ARRAY_SIZE(r8a7740_dmae_slaves), + .channel = r8a7740_dmae_channels, + .channel_num = ARRAY_SIZE(r8a7740_dmae_channels), + .ts_low_shift = TS_LOW_SHIFT, + .ts_low_mask = TS_LOW_BIT << TS_LOW_SHIFT, + .ts_high_shift = TS_HI_SHIFT, + .ts_high_mask = TS_HI_BIT << TS_HI_SHIFT, + .ts_shift = dma_ts_shift, + .ts_shift_num = ARRAY_SIZE(dma_ts_shift), + .dmaor_init = DMAOR_DME, + .chclr_present = 1, +}; + +/* Resource order important! */ +static struct resource r8a7740_dmae0_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xfe008020, + .end = 0xfe00828f, + .flags = IORESOURCE_MEM, + }, + { + /* DMARSx */ + .start = 0xfe009000, + .end = 0xfe00900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = gic_spi(34), + .end = gic_spi(34), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-5 */ + .start = gic_spi(28), + .end = gic_spi(33), + .flags = IORESOURCE_IRQ, + }, +}; + +/* Resource order important! */ +static struct resource r8a7740_dmae1_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xfe018020, + .end = 0xfe01828f, + .flags = IORESOURCE_MEM, + }, + { + /* DMARSx */ + .start = 0xfe019000, + .end = 0xfe01900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = gic_spi(41), + .end = gic_spi(41), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-5 */ + .start = gic_spi(35), + .end = gic_spi(40), + .flags = IORESOURCE_IRQ, + }, +}; + +/* Resource order important! */ +static struct resource r8a7740_dmae2_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xfe028020, + .end = 0xfe02828f, + .flags = IORESOURCE_MEM, + }, + { + /* DMARSx */ + .start = 0xfe029000, + .end = 0xfe02900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = gic_spi(48), + .end = gic_spi(48), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-5 */ + .start = gic_spi(42), + .end = gic_spi(47), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = r8a7740_dmae0_resources, + .num_resources = ARRAY_SIZE(r8a7740_dmae0_resources), + .dev = { + .platform_data = &dma_platform_data, + }, +}; + +static struct platform_device dma1_device = { + .name = "sh-dma-engine", + .id = 1, + .resource = r8a7740_dmae1_resources, + .num_resources = ARRAY_SIZE(r8a7740_dmae1_resources), + .dev = { + .platform_data = &dma_platform_data, + }, +}; + +static struct platform_device dma2_device = { + .name = "sh-dma-engine", + .id = 2, + .resource = r8a7740_dmae2_resources, + .num_resources = ARRAY_SIZE(r8a7740_dmae2_resources), + .dev = { + .platform_data = &dma_platform_data, + }, +}; + +/* USB-DMAC */ +static const struct sh_dmae_channel r8a7740_usb_dma_channels[] = { + { + .offset = 0, + }, { + .offset = 0x20, + }, +}; + +static const struct sh_dmae_slave_config r8a7740_usb_dma_slaves[] = { + { + .slave_id = SHDMA_SLAVE_USBHS_TX, + .chcr = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE), + }, { + .slave_id = SHDMA_SLAVE_USBHS_RX, + .chcr = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE), + }, +}; + +static struct sh_dmae_pdata usb_dma_platform_data = { + .slave = r8a7740_usb_dma_slaves, + .slave_num = ARRAY_SIZE(r8a7740_usb_dma_slaves), + .channel = r8a7740_usb_dma_channels, + .channel_num = ARRAY_SIZE(r8a7740_usb_dma_channels), + .ts_low_shift = USBTS_LOW_SHIFT, + .ts_low_mask = USBTS_LOW_BIT << USBTS_LOW_SHIFT, + .ts_high_shift = USBTS_HI_SHIFT, + .ts_high_mask = USBTS_HI_BIT << USBTS_HI_SHIFT, + .ts_shift = dma_usbts_shift, + .ts_shift_num = ARRAY_SIZE(dma_usbts_shift), + .dmaor_init = DMAOR_DME, + .chcr_offset = 0x14, + .chcr_ie_bit = 1 << 5, + .dmaor_is_32bit = 1, + .needs_tend_set = 1, + .no_dmars = 1, + .slave_only = 1, +}; + +static struct resource r8a7740_usb_dma_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xe68a0020, + .end = 0xe68a0064 - 1, + .flags = IORESOURCE_MEM, + }, + { + /* VCR/SWR/DMICR */ + .start = 0xe68a0000, + .end = 0xe68a0014 - 1, + .flags = IORESOURCE_MEM, + }, + { + /* IRQ for channels */ + .start = gic_spi(49), + .end = gic_spi(49), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usb_dma_device = { + .name = "sh-dma-engine", + .id = 3, + .resource = r8a7740_usb_dma_resources, + .num_resources = ARRAY_SIZE(r8a7740_usb_dma_resources), + .dev = { + .platform_data = &usb_dma_platform_data, + }, +}; + +/* I2C */ +static struct resource i2c0_resources[] = { + [0] = { + .name = "IIC0", + .start = 0xfff20000, + .end = 0xfff20425 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(201), + .end = gic_spi(204), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource i2c1_resources[] = { + [0] = { + .name = "IIC1", + .start = 0xe6c20000, + .end = 0xe6c20425 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(70), /* IIC1_ALI1 */ + .end = gic_spi(73), /* IIC1_DTEI1 */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c0_device = { + .name = "i2c-sh_mobile", + .id = 0, + .resource = i2c0_resources, + .num_resources = ARRAY_SIZE(i2c0_resources), +}; + +static struct platform_device i2c1_device = { + .name = "i2c-sh_mobile", + .id = 1, + .resource = i2c1_resources, + .num_resources = ARRAY_SIZE(i2c1_resources), +}; + +static struct resource pmu_resources[] = { + [0] = { + .start = gic_spi(83), + .end = gic_spi(83), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device pmu_device = { + .name = "armv7-pmu", + .id = -1, + .num_resources = ARRAY_SIZE(pmu_resources), + .resource = pmu_resources, +}; + +static struct platform_device *r8a7740_late_devices[] __initdata = { + &i2c0_device, + &i2c1_device, + &dma0_device, + &dma1_device, + &dma2_device, + &usb_dma_device, + &pmu_device, +}; + +/* + * r8a7740 chip has lasting errata on MERAM buffer. + * this is work-around for it. + * see + * "Media RAM (MERAM)" on r8a7740 documentation + */ +#define MEBUFCNTR 0xFE950098 +void __init r8a7740_meram_workaround(void) +{ + void __iomem *reg; + + reg = ioremap_nocache(MEBUFCNTR, 4); + if (reg) { + iowrite32(0x01600164, reg); + iounmap(reg); + } +} + +#define ICCR 0x0004 +#define ICSTART 0x0070 + +#define i2c_read(reg, offset) ioread8(reg + offset) +#define i2c_write(reg, offset, data) iowrite8(data, reg + offset) + +/* + * r8a7740 chip has lasting errata on I2C I/O pad reset. + * this is work-around for it. + */ +static void r8a7740_i2c_workaround(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *reg; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + pr_err("r8a7740 i2c workaround fail (cannot find resource)\n"); + return; + } + + reg = ioremap(res->start, resource_size(res)); + if (unlikely(!reg)) { + pr_err("r8a7740 i2c workaround fail (cannot map IO)\n"); + return; + } + + i2c_write(reg, ICCR, i2c_read(reg, ICCR) | 0x80); + i2c_read(reg, ICCR); /* dummy read */ + + i2c_write(reg, ICSTART, i2c_read(reg, ICSTART) | 0x10); + i2c_read(reg, ICSTART); /* dummy read */ + + udelay(10); + + i2c_write(reg, ICCR, 0x01); + i2c_write(reg, ICSTART, 0x00); + + udelay(10); + + i2c_write(reg, ICCR, 0x10); + udelay(10); + i2c_write(reg, ICCR, 0x00); + udelay(10); + i2c_write(reg, ICCR, 0x10); + udelay(10); + + iounmap(reg); +} + +void __init r8a7740_add_standard_devices(void) +{ + static struct pm_domain_device domain_devices[] __initdata = { + { "A4R", &tmu0_device }, + { "A4R", &i2c0_device }, + { "A4S", &irqpin0_device }, + { "A4S", &irqpin1_device }, + { "A4S", &irqpin2_device }, + { "A4S", &irqpin3_device }, + { "A3SP", &scif0_device }, + { "A3SP", &scif1_device }, + { "A3SP", &scif2_device }, + { "A3SP", &scif3_device }, + { "A3SP", &scif4_device }, + { "A3SP", &scif5_device }, + { "A3SP", &scif6_device }, + { "A3SP", &scif7_device }, + { "A3SP", &scif8_device }, + { "A3SP", &i2c1_device }, + { "A3SP", &ipmmu_device }, + { "A3SP", &dma0_device }, + { "A3SP", &dma1_device }, + { "A3SP", &dma2_device }, + { "A3SP", &usb_dma_device }, + }; + + /* I2C work-around */ + r8a7740_i2c_workaround(&i2c0_device); + r8a7740_i2c_workaround(&i2c1_device); + + r8a7740_init_pm_domains(); + + /* add devices */ + platform_add_devices(r8a7740_early_devices, + ARRAY_SIZE(r8a7740_early_devices)); + platform_add_devices(r8a7740_late_devices, + ARRAY_SIZE(r8a7740_late_devices)); + + /* add devices to PM domain */ + rmobile_add_devices_to_domains(domain_devices, + ARRAY_SIZE(domain_devices)); +} + +void __init r8a7740_add_early_devices(void) +{ + early_platform_add_devices(r8a7740_early_devices, + ARRAY_SIZE(r8a7740_early_devices)); + + /* setup early console here as well */ + shmobile_setup_console(); +} + +#ifdef CONFIG_USE_OF + +void __init r8a7740_init_irq_of(void) +{ + void __iomem *intc_prio_base = ioremap_nocache(0xe6900010, 0x10); + void __iomem *intc_msk_base = ioremap_nocache(0xe6900040, 0x10); + void __iomem *pfc_inta_ctrl = ioremap_nocache(0xe605807c, 0x4); + +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + void __iomem *gic_dist_base = ioremap_nocache(0xc2800000, 0x1000); + void __iomem *gic_cpu_base = ioremap_nocache(0xc2000000, 0x1000); + + gic_init(0, 29, gic_dist_base, gic_cpu_base); +#else + irqchip_init(); +#endif + + /* route signals to GIC */ + iowrite32(0x0, pfc_inta_ctrl); + + /* + * To mask the shared interrupt to SPI 149 we must ensure to set + * PRIO *and* MASK. Else we run into IRQ floods when registering + * the intc_irqpin devices + */ + iowrite32(0x0, intc_prio_base + 0x0); + iowrite32(0x0, intc_prio_base + 0x4); + iowrite32(0x0, intc_prio_base + 0x8); + iowrite32(0x0, intc_prio_base + 0xc); + iowrite8(0xff, intc_msk_base + 0x0); + iowrite8(0xff, intc_msk_base + 0x4); + iowrite8(0xff, intc_msk_base + 0x8); + iowrite8(0xff, intc_msk_base + 0xc); + + iounmap(intc_prio_base); + iounmap(intc_msk_base); + iounmap(pfc_inta_ctrl); +} + +static void __init r8a7740_generic_init(void) +{ + r8a7740_meram_workaround(); + +#ifdef CONFIG_CACHE_L2X0 + /* Shared attribute override enable, 32K*8way */ + l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff); +#endif + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *r8a7740_boards_compat_dt[] __initdata = { + "renesas,r8a7740", + NULL, +}; + +DT_MACHINE_START(R8A7740_DT, "Generic R8A7740 (Flattened Device Tree)") + .map_io = r8a7740_map_io, + .init_early = shmobile_init_delay, + .init_irq = r8a7740_init_irq_of, + .init_machine = r8a7740_generic_init, + .init_late = shmobile_init_late, + .dt_compat = r8a7740_boards_compat_dt, +MACHINE_END + +#endif /* CONFIG_USE_OF */ diff --git a/arch/arm/mach-shmobile/setup-r8a7778.c b/arch/arm/mach-shmobile/setup-r8a7778.c new file mode 100644 index 000000000..c49aa094f --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7778.c @@ -0,0 +1,633 @@ +/* + * r8a7778 processor support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * 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. + */ + +#include <linux/clk/shmobile.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_data/dma-rcar-hpbdma.h> +#include <linux/platform_data/gpio-rcar.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> +#include <linux/platform_device.h> +#include <linux/irqchip.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/pm_runtime.h> +#include <linux/usb/phy.h> +#include <linux/usb/hcd.h> +#include <linux/usb/ehci_pdriver.h> +#include <linux/usb/ohci_pdriver.h> +#include <linux/dma-mapping.h> + +#include <asm/mach/arch.h> +#include <asm/hardware/cache-l2x0.h> + +#include "common.h" +#include "irqs.h" +#include "r8a7778.h" + +#define MODEMR 0xffcc0020 + +#ifdef CONFIG_COMMON_CLK +static void __init r8a7778_timer_init(void) +{ + u32 mode; + void __iomem *modemr = ioremap_nocache(MODEMR, 4); + + BUG_ON(!modemr); + mode = ioread32(modemr); + iounmap(modemr); + r8a7778_clocks_init(mode); +} +#endif + +/* SCIF */ +#define R8A7778_SCIF(index, baseaddr, irq) \ +static struct plat_sci_port scif##index##_platform_data = { \ + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \ + .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE1, \ + .type = PORT_SCIF, \ +}; \ + \ +static struct resource scif##index##_resources[] = { \ + DEFINE_RES_MEM(baseaddr, 0x100), \ + DEFINE_RES_IRQ(irq), \ +} + +R8A7778_SCIF(0, 0xffe40000, gic_iid(0x66)); +R8A7778_SCIF(1, 0xffe41000, gic_iid(0x67)); +R8A7778_SCIF(2, 0xffe42000, gic_iid(0x68)); +R8A7778_SCIF(3, 0xffe43000, gic_iid(0x69)); +R8A7778_SCIF(4, 0xffe44000, gic_iid(0x6a)); +R8A7778_SCIF(5, 0xffe45000, gic_iid(0x6b)); + +#define r8a7778_register_scif(index) \ + platform_device_register_resndata(NULL, "sh-sci", index, \ + scif##index##_resources, \ + ARRAY_SIZE(scif##index##_resources), \ + &scif##index##_platform_data, \ + sizeof(scif##index##_platform_data)) + +/* TMU */ +static struct sh_timer_config sh_tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource sh_tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(gic_iid(0x40)), + DEFINE_RES_IRQ(gic_iid(0x41)), + DEFINE_RES_IRQ(gic_iid(0x42)), +}; + +#define r8a7778_register_tmu(idx) \ + platform_device_register_resndata( \ + NULL, "sh-tmu", idx, \ + sh_tmu##idx##_resources, \ + ARRAY_SIZE(sh_tmu##idx##_resources), \ + &sh_tmu##idx##_platform_data, \ + sizeof(sh_tmu##idx##_platform_data)) + +int r8a7778_usb_phy_power(bool enable) +{ + static struct usb_phy *phy = NULL; + int ret = 0; + + if (!phy) + phy = usb_get_phy(USB_PHY_TYPE_USB2); + + if (IS_ERR(phy)) { + pr_err("kernel doesn't have usb phy driver\n"); + return PTR_ERR(phy); + } + + if (enable) + ret = usb_phy_init(phy); + else + usb_phy_shutdown(phy); + + return ret; +} + +/* USB */ +static int usb_power_on(struct platform_device *pdev) +{ + int ret = r8a7778_usb_phy_power(true); + + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + return 0; +} + +static void usb_power_off(struct platform_device *pdev) +{ + if (r8a7778_usb_phy_power(false)) + return; + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +} + +static int ehci_init_internal_buffer(struct usb_hcd *hcd) +{ + /* + * Below are recommended values from the datasheet; + * see [USB :: Setting of EHCI Internal Buffer]. + */ + /* EHCI IP internal buffer setting */ + iowrite32(0x00ff0040, hcd->regs + 0x0094); + /* EHCI IP internal buffer enable */ + iowrite32(0x00000001, hcd->regs + 0x009C); + + return 0; +} + +static struct usb_ehci_pdata ehci_pdata __initdata = { + .power_on = usb_power_on, + .power_off = usb_power_off, + .power_suspend = usb_power_off, + .pre_setup = ehci_init_internal_buffer, +}; + +static struct resource ehci_resources[] __initdata = { + DEFINE_RES_MEM(0xffe70000, 0x400), + DEFINE_RES_IRQ(gic_iid(0x4c)), +}; + +static struct usb_ohci_pdata ohci_pdata __initdata = { + .power_on = usb_power_on, + .power_off = usb_power_off, + .power_suspend = usb_power_off, +}; + +static struct resource ohci_resources[] __initdata = { + DEFINE_RES_MEM(0xffe70400, 0x400), + DEFINE_RES_IRQ(gic_iid(0x4c)), +}; + +#define USB_PLATFORM_INFO(hci) \ +static struct platform_device_info hci##_info __initdata = { \ + .name = #hci "-platform", \ + .id = -1, \ + .res = hci##_resources, \ + .num_res = ARRAY_SIZE(hci##_resources), \ + .data = &hci##_pdata, \ + .size_data = sizeof(hci##_pdata), \ + .dma_mask = DMA_BIT_MASK(32), \ +} + +USB_PLATFORM_INFO(ehci); +USB_PLATFORM_INFO(ohci); + +/* PFC/GPIO */ +static struct resource pfc_resources[] __initdata = { + DEFINE_RES_MEM(0xfffc0000, 0x118), +}; + +#define R8A7778_GPIO(idx) \ +static struct resource r8a7778_gpio##idx##_resources[] __initdata = { \ + DEFINE_RES_MEM(0xffc40000 + 0x1000 * (idx), 0x30), \ + DEFINE_RES_IRQ(gic_iid(0x87)), \ +}; \ + \ +static struct gpio_rcar_config r8a7778_gpio##idx##_platform_data __initdata = { \ + .gpio_base = 32 * (idx), \ + .irq_base = GPIO_IRQ_BASE(idx), \ + .number_of_pins = 32, \ + .pctl_name = "pfc-r8a7778", \ +} + +R8A7778_GPIO(0); +R8A7778_GPIO(1); +R8A7778_GPIO(2); +R8A7778_GPIO(3); +R8A7778_GPIO(4); + +#define r8a7778_register_gpio(idx) \ + platform_device_register_resndata( \ + NULL, "gpio_rcar", idx, \ + r8a7778_gpio##idx##_resources, \ + ARRAY_SIZE(r8a7778_gpio##idx##_resources), \ + &r8a7778_gpio##idx##_platform_data, \ + sizeof(r8a7778_gpio##idx##_platform_data)) + +void __init r8a7778_pinmux_init(void) +{ + platform_device_register_simple( + "pfc-r8a7778", -1, + pfc_resources, + ARRAY_SIZE(pfc_resources)); + + r8a7778_register_gpio(0); + r8a7778_register_gpio(1); + r8a7778_register_gpio(2); + r8a7778_register_gpio(3); + r8a7778_register_gpio(4); +}; + +/* I2C */ +static struct resource i2c_resources[] __initdata = { + /* I2C0 */ + DEFINE_RES_MEM(0xffc70000, 0x1000), + DEFINE_RES_IRQ(gic_iid(0x63)), + /* I2C1 */ + DEFINE_RES_MEM(0xffc71000, 0x1000), + DEFINE_RES_IRQ(gic_iid(0x6e)), + /* I2C2 */ + DEFINE_RES_MEM(0xffc72000, 0x1000), + DEFINE_RES_IRQ(gic_iid(0x6c)), + /* I2C3 */ + DEFINE_RES_MEM(0xffc73000, 0x1000), + DEFINE_RES_IRQ(gic_iid(0x6d)), +}; + +static void __init r8a7778_register_i2c(int id) +{ + BUG_ON(id < 0 || id > 3); + + platform_device_register_simple( + "i2c-rcar", id, + i2c_resources + (2 * id), 2); +} + +/* HSPI */ +static struct resource hspi_resources[] __initdata = { + /* HSPI0 */ + DEFINE_RES_MEM(0xfffc7000, 0x18), + DEFINE_RES_IRQ(gic_iid(0x5f)), + /* HSPI1 */ + DEFINE_RES_MEM(0xfffc8000, 0x18), + DEFINE_RES_IRQ(gic_iid(0x74)), + /* HSPI2 */ + DEFINE_RES_MEM(0xfffc6000, 0x18), + DEFINE_RES_IRQ(gic_iid(0x75)), +}; + +static void __init r8a7778_register_hspi(int id) +{ + BUG_ON(id < 0 || id > 2); + + platform_device_register_simple( + "sh-hspi", id, + hspi_resources + (2 * id), 2); +} + +void __init r8a7778_add_dt_devices(void) +{ +#ifdef CONFIG_CACHE_L2X0 + void __iomem *base = ioremap_nocache(0xf0100000, 0x1000); + if (base) { + /* + * Shared attribute override enable, 64K*16way + * don't call iounmap(base) + */ + l2x0_init(base, 0x00400000, 0xc20f0fff); + } +#endif +} + +/* HPB-DMA */ + +/* Asynchronous mode register (ASYNCMDR) bits */ +#define HPB_DMAE_ASYNCMDR_ASMD22_MASK BIT(2) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD22_SINGLE BIT(2) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD22_MULTI 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD21_MASK BIT(1) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD21_SINGLE BIT(1) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD21_MULTI 0 /* SDHI0 */ + +#define HPBDMA_SSI(_id) \ +{ \ + .id = HPBDMA_SLAVE_SSI## _id ##_TX, \ + .addr = 0xffd91008 + (_id * 0x40), \ + .dcr = HPB_DMAE_DCR_CT | \ + HPB_DMAE_DCR_DIP | \ + HPB_DMAE_DCR_SPDS_32BIT | \ + HPB_DMAE_DCR_DMDL | \ + HPB_DMAE_DCR_DPDS_32BIT, \ + .port = _id + (_id << 8), \ + .dma_ch = (28 + _id), \ +}, { \ + .id = HPBDMA_SLAVE_SSI## _id ##_RX, \ + .addr = 0xffd9100c + (_id * 0x40), \ + .dcr = HPB_DMAE_DCR_CT | \ + HPB_DMAE_DCR_DIP | \ + HPB_DMAE_DCR_SMDL | \ + HPB_DMAE_DCR_SPDS_32BIT | \ + HPB_DMAE_DCR_DPDS_32BIT, \ + .port = _id + (_id << 8), \ + .dma_ch = (28 + _id), \ +} + +#define HPBDMA_HPBIF(_id) \ +{ \ + .id = HPBDMA_SLAVE_HPBIF## _id ##_TX, \ + .addr = 0xffda0000 + (_id * 0x1000), \ + .dcr = HPB_DMAE_DCR_CT | \ + HPB_DMAE_DCR_DIP | \ + HPB_DMAE_DCR_SPDS_32BIT | \ + HPB_DMAE_DCR_DMDL | \ + HPB_DMAE_DCR_DPDS_32BIT, \ + .port = 0x1111, \ + .dma_ch = (28 + _id), \ +}, { \ + .id = HPBDMA_SLAVE_HPBIF## _id ##_RX, \ + .addr = 0xffda0000 + (_id * 0x1000), \ + .dcr = HPB_DMAE_DCR_CT | \ + HPB_DMAE_DCR_DIP | \ + HPB_DMAE_DCR_SMDL | \ + HPB_DMAE_DCR_SPDS_32BIT | \ + HPB_DMAE_DCR_DPDS_32BIT, \ + .port = 0x1111, \ + .dma_ch = (28 + _id), \ +} + +static const struct hpb_dmae_slave_config hpb_dmae_slaves[] = { + { + .id = HPBDMA_SLAVE_SDHI0_TX, + .addr = 0xffe4c000 + 0x30, + .dcr = HPB_DMAE_DCR_SPDS_16BIT | + HPB_DMAE_DCR_DMDL | + HPB_DMAE_DCR_DPDS_16BIT, + .rstr = HPB_DMAE_ASYNCRSTR_ASRST21 | + HPB_DMAE_ASYNCRSTR_ASRST22 | + HPB_DMAE_ASYNCRSTR_ASRST23, + .mdr = HPB_DMAE_ASYNCMDR_ASMD21_MULTI, + .mdm = HPB_DMAE_ASYNCMDR_ASMD21_MASK, + .port = 0x0D0C, + .flags = HPB_DMAE_SET_ASYNC_RESET | HPB_DMAE_SET_ASYNC_MODE, + .dma_ch = 21, + }, { + .id = HPBDMA_SLAVE_SDHI0_RX, + .addr = 0xffe4c000 + 0x30, + .dcr = HPB_DMAE_DCR_SMDL | + HPB_DMAE_DCR_SPDS_16BIT | + HPB_DMAE_DCR_DPDS_16BIT, + .rstr = HPB_DMAE_ASYNCRSTR_ASRST21 | + HPB_DMAE_ASYNCRSTR_ASRST22 | + HPB_DMAE_ASYNCRSTR_ASRST23, + .mdr = HPB_DMAE_ASYNCMDR_ASMD22_MULTI, + .mdm = HPB_DMAE_ASYNCMDR_ASMD22_MASK, + .port = 0x0D0C, + .flags = HPB_DMAE_SET_ASYNC_RESET | HPB_DMAE_SET_ASYNC_MODE, + .dma_ch = 22, + }, { + .id = HPBDMA_SLAVE_USBFUNC_TX, /* for D0 */ + .addr = 0xffe60018, + .dcr = HPB_DMAE_DCR_SPDS_32BIT | + HPB_DMAE_DCR_DMDL | + HPB_DMAE_DCR_DPDS_32BIT, + .port = 0x0000, + .dma_ch = 14, + }, { + .id = HPBDMA_SLAVE_USBFUNC_RX, /* for D1 */ + .addr = 0xffe6001c, + .dcr = HPB_DMAE_DCR_SMDL | + HPB_DMAE_DCR_SPDS_32BIT | + HPB_DMAE_DCR_DPDS_32BIT, + .port = 0x0101, + .dma_ch = 15, + }, + + HPBDMA_SSI(0), + HPBDMA_SSI(1), + HPBDMA_SSI(2), + HPBDMA_SSI(3), + HPBDMA_SSI(4), + HPBDMA_SSI(5), + HPBDMA_SSI(6), + HPBDMA_SSI(7), + HPBDMA_SSI(8), + + HPBDMA_HPBIF(0), + HPBDMA_HPBIF(1), + HPBDMA_HPBIF(2), + HPBDMA_HPBIF(3), + HPBDMA_HPBIF(4), + HPBDMA_HPBIF(5), + HPBDMA_HPBIF(6), + HPBDMA_HPBIF(7), + HPBDMA_HPBIF(8), +}; + +static const struct hpb_dmae_channel hpb_dmae_channels[] = { + HPB_DMAE_CHANNEL(0x7c, HPBDMA_SLAVE_USBFUNC_TX), /* ch. 14 */ + HPB_DMAE_CHANNEL(0x7c, HPBDMA_SLAVE_USBFUNC_RX), /* ch. 15 */ + HPB_DMAE_CHANNEL(0x7e, HPBDMA_SLAVE_SDHI0_TX), /* ch. 21 */ + HPB_DMAE_CHANNEL(0x7e, HPBDMA_SLAVE_SDHI0_RX), /* ch. 22 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI0_TX), /* ch. 28 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI0_RX), /* ch. 28 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF0_TX), /* ch. 28 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF0_RX), /* ch. 28 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI1_TX), /* ch. 29 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI1_RX), /* ch. 29 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF1_TX), /* ch. 29 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF1_RX), /* ch. 29 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI2_TX), /* ch. 30 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI2_RX), /* ch. 30 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF2_TX), /* ch. 30 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF2_RX), /* ch. 30 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI3_TX), /* ch. 31 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI3_RX), /* ch. 31 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF3_TX), /* ch. 31 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF3_RX), /* ch. 31 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI4_TX), /* ch. 32 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI4_RX), /* ch. 32 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF4_TX), /* ch. 32 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF4_RX), /* ch. 32 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI5_TX), /* ch. 33 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI5_RX), /* ch. 33 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF5_TX), /* ch. 33 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF5_RX), /* ch. 33 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI6_TX), /* ch. 34 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI6_RX), /* ch. 34 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF6_TX), /* ch. 34 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF6_RX), /* ch. 34 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI7_TX), /* ch. 35 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI7_RX), /* ch. 35 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF7_TX), /* ch. 35 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF7_RX), /* ch. 35 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI8_TX), /* ch. 36 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_SSI8_RX), /* ch. 36 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF8_TX), /* ch. 36 */ + HPB_DMAE_CHANNEL(0x7f, HPBDMA_SLAVE_HPBIF8_RX), /* ch. 36 */ +}; + +static struct hpb_dmae_pdata dma_platform_data __initdata = { + .slaves = hpb_dmae_slaves, + .num_slaves = ARRAY_SIZE(hpb_dmae_slaves), + .channels = hpb_dmae_channels, + .num_channels = ARRAY_SIZE(hpb_dmae_channels), + .ts_shift = { + [XMIT_SZ_8BIT] = 0, + [XMIT_SZ_16BIT] = 1, + [XMIT_SZ_32BIT] = 2, + }, + .num_hw_channels = 39, +}; + +static struct resource hpb_dmae_resources[] __initdata = { + /* Channel registers */ + DEFINE_RES_MEM(0xffc08000, 0x1000), + /* Common registers */ + DEFINE_RES_MEM(0xffc09000, 0x170), + /* Asynchronous reset registers */ + DEFINE_RES_MEM(0xffc00300, 4), + /* Asynchronous mode registers */ + DEFINE_RES_MEM(0xffc00400, 4), + /* IRQ for DMA channels */ + DEFINE_RES_NAMED(gic_iid(0x7b), 5, NULL, IORESOURCE_IRQ), +}; + +static void __init r8a7778_register_hpb_dmae(void) +{ + platform_device_register_resndata(NULL, "hpb-dma-engine", + -1, hpb_dmae_resources, + ARRAY_SIZE(hpb_dmae_resources), + &dma_platform_data, + sizeof(dma_platform_data)); +} + +void __init r8a7778_add_standard_devices(void) +{ + r8a7778_add_dt_devices(); + r8a7778_register_tmu(0); + r8a7778_register_scif(0); + r8a7778_register_scif(1); + r8a7778_register_scif(2); + r8a7778_register_scif(3); + r8a7778_register_scif(4); + r8a7778_register_scif(5); + r8a7778_register_i2c(0); + r8a7778_register_i2c(1); + r8a7778_register_i2c(2); + r8a7778_register_i2c(3); + r8a7778_register_hspi(0); + r8a7778_register_hspi(1); + r8a7778_register_hspi(2); + + r8a7778_register_hpb_dmae(); +} + +void __init r8a7778_init_late(void) +{ + shmobile_init_late(); + platform_device_register_full(&ehci_info); + platform_device_register_full(&ohci_info); +} + +static struct renesas_intc_irqpin_config irqpin_platform_data __initdata = { + .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */ + .sense_bitfield_width = 2, +}; + +static struct resource irqpin_resources[] __initdata = { + DEFINE_RES_MEM(0xfe78001c, 4), /* ICR1 */ + DEFINE_RES_MEM(0xfe780010, 4), /* INTPRI */ + DEFINE_RES_MEM(0xfe780024, 4), /* INTREQ */ + DEFINE_RES_MEM(0xfe780044, 4), /* INTMSK0 */ + DEFINE_RES_MEM(0xfe780064, 4), /* INTMSKCLR0 */ + DEFINE_RES_IRQ(gic_iid(0x3b)), /* IRQ0 */ + DEFINE_RES_IRQ(gic_iid(0x3c)), /* IRQ1 */ + DEFINE_RES_IRQ(gic_iid(0x3d)), /* IRQ2 */ + DEFINE_RES_IRQ(gic_iid(0x3e)), /* IRQ3 */ +}; + +void __init r8a7778_init_irq_extpin_dt(int irlm) +{ + void __iomem *icr0 = ioremap_nocache(0xfe780000, PAGE_SIZE); + unsigned long tmp; + + if (!icr0) { + pr_warn("r8a7778: unable to setup external irq pin mode\n"); + return; + } + + tmp = ioread32(icr0); + if (irlm) + tmp |= 1 << 23; /* IRQ0 -> IRQ3 as individual pins */ + else + tmp &= ~(1 << 23); /* IRL mode - not supported */ + tmp |= (1 << 21); /* LVLMODE = 1 */ + iowrite32(tmp, icr0); + iounmap(icr0); +} + +void __init r8a7778_init_irq_extpin(int irlm) +{ + r8a7778_init_irq_extpin_dt(irlm); + if (irlm) + platform_device_register_resndata( + NULL, "renesas_intc_irqpin", -1, + irqpin_resources, ARRAY_SIZE(irqpin_resources), + &irqpin_platform_data, sizeof(irqpin_platform_data)); +} + +#ifdef CONFIG_USE_OF +#define INT2SMSKCR0 0x82288 /* 0xfe782288 */ +#define INT2SMSKCR1 0x8228c /* 0xfe78228c */ + +#define INT2NTSR0 0x00018 /* 0xfe700018 */ +#define INT2NTSR1 0x0002c /* 0xfe70002c */ +void __init r8a7778_init_irq_dt(void) +{ + void __iomem *base = ioremap_nocache(0xfe700000, 0x00100000); +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + void __iomem *gic_dist_base = ioremap_nocache(0xfe438000, 0x1000); + void __iomem *gic_cpu_base = ioremap_nocache(0xfe430000, 0x1000); +#endif + + BUG_ON(!base); + +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + gic_init(0, 29, gic_dist_base, gic_cpu_base); +#else + irqchip_init(); +#endif + /* route all interrupts to ARM */ + __raw_writel(0x73ffffff, base + INT2NTSR0); + __raw_writel(0xffffffff, base + INT2NTSR1); + + /* unmask all known interrupts in INTCS2 */ + __raw_writel(0x08330773, base + INT2SMSKCR0); + __raw_writel(0x00311110, base + INT2SMSKCR1); + + iounmap(base); +} + +static const char *r8a7778_compat_dt[] __initdata = { + "renesas,r8a7778", + NULL, +}; + +DT_MACHINE_START(R8A7778_DT, "Generic R8A7778 (Flattened Device Tree)") + .init_early = shmobile_init_delay, + .init_irq = r8a7778_init_irq_dt, + .init_late = shmobile_init_late, +#ifdef CONFIG_COMMON_CLK + .init_time = r8a7778_timer_init, +#endif + .dt_compat = r8a7778_compat_dt, +MACHINE_END + +#endif /* CONFIG_USE_OF */ diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c new file mode 100644 index 000000000..c03e562be --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -0,0 +1,771 @@ +/* + * r8a7779 processor support + * + * Copyright (C) 2011, 2013 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/of_platform.h> +#include <linux/platform_data/dma-rcar-hpbdma.h> +#include <linux/platform_data/gpio-rcar.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/dma-mapping.h> +#include <linux/usb/otg.h> +#include <linux/usb/hcd.h> +#include <linux/usb/ehci_pdriver.h> +#include <linux/usb/ohci_pdriver.h> +#include <linux/pm_runtime.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/time.h> +#include <asm/mach/map.h> +#include <asm/hardware/cache-l2x0.h> + +#include "common.h" +#include "irqs.h" +#include "r8a7779.h" + +static struct map_desc r8a7779_io_desc[] __initdata = { + /* 2M identity mapping for 0xf0000000 (MPCORE) */ + { + .virtual = 0xf0000000, + .pfn = __phys_to_pfn(0xf0000000), + .length = SZ_2M, + .type = MT_DEVICE_NONSHARED + }, + /* 16M identity mapping for 0xfexxxxxx (DMAC-S/HPBREG/INTC2/LRAM/DBSC) */ + { + .virtual = 0xfe000000, + .pfn = __phys_to_pfn(0xfe000000), + .length = SZ_16M, + .type = MT_DEVICE_NONSHARED + }, +}; + +void __init r8a7779_map_io(void) +{ + debug_ll_io_init(); + iotable_init(r8a7779_io_desc, ARRAY_SIZE(r8a7779_io_desc)); +} + +/* IRQ */ +#define INT2SMSKCR0 IOMEM(0xfe7822a0) +#define INT2SMSKCR1 IOMEM(0xfe7822a4) +#define INT2SMSKCR2 IOMEM(0xfe7822a8) +#define INT2SMSKCR3 IOMEM(0xfe7822ac) +#define INT2SMSKCR4 IOMEM(0xfe7822b0) + +#define INT2NTSR0 IOMEM(0xfe700060) +#define INT2NTSR1 IOMEM(0xfe700064) + +static struct renesas_intc_irqpin_config irqpin0_platform_data __initdata = { + .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */ + .sense_bitfield_width = 2, +}; + +static struct resource irqpin0_resources[] __initdata = { + DEFINE_RES_MEM(0xfe78001c, 4), /* ICR1 */ + DEFINE_RES_MEM(0xfe780010, 4), /* INTPRI */ + DEFINE_RES_MEM(0xfe780024, 4), /* INTREQ */ + DEFINE_RES_MEM(0xfe780044, 4), /* INTMSK0 */ + DEFINE_RES_MEM(0xfe780064, 4), /* INTMSKCLR0 */ + DEFINE_RES_IRQ(gic_spi(27)), /* IRQ0 */ + DEFINE_RES_IRQ(gic_spi(28)), /* IRQ1 */ + DEFINE_RES_IRQ(gic_spi(29)), /* IRQ2 */ + DEFINE_RES_IRQ(gic_spi(30)), /* IRQ3 */ +}; + +void __init r8a7779_init_irq_extpin_dt(int irlm) +{ + void __iomem *icr0 = ioremap_nocache(0xfe780000, PAGE_SIZE); + u32 tmp; + + if (!icr0) { + pr_warn("r8a7779: unable to setup external irq pin mode\n"); + return; + } + + tmp = ioread32(icr0); + if (irlm) + tmp |= 1 << 23; /* IRQ0 -> IRQ3 as individual pins */ + else + tmp &= ~(1 << 23); /* IRL mode - not supported */ + tmp |= (1 << 21); /* LVLMODE = 1 */ + iowrite32(tmp, icr0); + iounmap(icr0); +} + +void __init r8a7779_init_irq_extpin(int irlm) +{ + r8a7779_init_irq_extpin_dt(irlm); + if (irlm) + platform_device_register_resndata( + NULL, "renesas_intc_irqpin", -1, + irqpin0_resources, ARRAY_SIZE(irqpin0_resources), + &irqpin0_platform_data, sizeof(irqpin0_platform_data)); +} + +/* PFC/GPIO */ +static struct resource r8a7779_pfc_resources[] = { + DEFINE_RES_MEM(0xfffc0000, 0x023c), +}; + +static struct platform_device r8a7779_pfc_device = { + .name = "pfc-r8a7779", + .id = -1, + .resource = r8a7779_pfc_resources, + .num_resources = ARRAY_SIZE(r8a7779_pfc_resources), +}; + +#define R8A7779_GPIO(idx, npins) \ +static struct resource r8a7779_gpio##idx##_resources[] = { \ + DEFINE_RES_MEM(0xffc40000 + (0x1000 * (idx)), 0x002c), \ + DEFINE_RES_IRQ(gic_iid(0xad + (idx))), \ +}; \ + \ +static struct gpio_rcar_config r8a7779_gpio##idx##_platform_data = { \ + .gpio_base = 32 * (idx), \ + .irq_base = 0, \ + .number_of_pins = npins, \ + .pctl_name = "pfc-r8a7779", \ +}; \ + \ +static struct platform_device r8a7779_gpio##idx##_device = { \ + .name = "gpio_rcar", \ + .id = idx, \ + .resource = r8a7779_gpio##idx##_resources, \ + .num_resources = ARRAY_SIZE(r8a7779_gpio##idx##_resources), \ + .dev = { \ + .platform_data = &r8a7779_gpio##idx##_platform_data, \ + }, \ +} + +R8A7779_GPIO(0, 32); +R8A7779_GPIO(1, 32); +R8A7779_GPIO(2, 32); +R8A7779_GPIO(3, 32); +R8A7779_GPIO(4, 32); +R8A7779_GPIO(5, 32); +R8A7779_GPIO(6, 9); + +static struct platform_device *r8a7779_pinctrl_devices[] __initdata = { + &r8a7779_pfc_device, + &r8a7779_gpio0_device, + &r8a7779_gpio1_device, + &r8a7779_gpio2_device, + &r8a7779_gpio3_device, + &r8a7779_gpio4_device, + &r8a7779_gpio5_device, + &r8a7779_gpio6_device, +}; + +void __init r8a7779_pinmux_init(void) +{ + platform_add_devices(r8a7779_pinctrl_devices, + ARRAY_SIZE(r8a7779_pinctrl_devices)); +} + +/* SCIF */ +#define R8A7779_SCIF(index, baseaddr, irq) \ +static struct plat_sci_port scif##index##_platform_data = { \ + .type = PORT_SCIF, \ + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \ + .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE1, \ +}; \ + \ +static struct resource scif##index##_resources[] = { \ + DEFINE_RES_MEM(baseaddr, 0x100), \ + DEFINE_RES_IRQ(irq), \ +}; \ + \ +static struct platform_device scif##index##_device = { \ + .name = "sh-sci", \ + .id = index, \ + .resource = scif##index##_resources, \ + .num_resources = ARRAY_SIZE(scif##index##_resources), \ + .dev = { \ + .platform_data = &scif##index##_platform_data, \ + }, \ +} + +R8A7779_SCIF(0, 0xffe40000, gic_iid(0x78)); +R8A7779_SCIF(1, 0xffe41000, gic_iid(0x79)); +R8A7779_SCIF(2, 0xffe42000, gic_iid(0x7a)); +R8A7779_SCIF(3, 0xffe43000, gic_iid(0x7b)); +R8A7779_SCIF(4, 0xffe44000, gic_iid(0x7c)); +R8A7779_SCIF(5, 0xffe45000, gic_iid(0x7d)); + +/* TMU */ +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(gic_iid(0x40)), + DEFINE_RES_IRQ(gic_iid(0x41)), + DEFINE_RES_IRQ(gic_iid(0x42)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +/* I2C */ +static struct resource rcar_i2c0_res[] = { + { + .start = 0xffc70000, + .end = 0xffc70fff, + .flags = IORESOURCE_MEM, + }, { + .start = gic_iid(0x6f), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c0_device = { + .name = "i2c-rcar", + .id = 0, + .resource = rcar_i2c0_res, + .num_resources = ARRAY_SIZE(rcar_i2c0_res), +}; + +static struct resource rcar_i2c1_res[] = { + { + .start = 0xffc71000, + .end = 0xffc71fff, + .flags = IORESOURCE_MEM, + }, { + .start = gic_iid(0x72), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c1_device = { + .name = "i2c-rcar", + .id = 1, + .resource = rcar_i2c1_res, + .num_resources = ARRAY_SIZE(rcar_i2c1_res), +}; + +static struct resource rcar_i2c2_res[] = { + { + .start = 0xffc72000, + .end = 0xffc72fff, + .flags = IORESOURCE_MEM, + }, { + .start = gic_iid(0x70), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c2_device = { + .name = "i2c-rcar", + .id = 2, + .resource = rcar_i2c2_res, + .num_resources = ARRAY_SIZE(rcar_i2c2_res), +}; + +static struct resource rcar_i2c3_res[] = { + { + .start = 0xffc73000, + .end = 0xffc73fff, + .flags = IORESOURCE_MEM, + }, { + .start = gic_iid(0x71), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c3_device = { + .name = "i2c-rcar", + .id = 3, + .resource = rcar_i2c3_res, + .num_resources = ARRAY_SIZE(rcar_i2c3_res), +}; + +static struct resource sata_resources[] = { + [0] = { + .name = "rcar-sata", + .start = 0xfc600000, + .end = 0xfc601fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_iid(0x84), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sata_device = { + .name = "sata_rcar", + .id = -1, + .resource = sata_resources, + .num_resources = ARRAY_SIZE(sata_resources), + .dev = { + .dma_mask = &sata_device.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +/* USB */ +static struct usb_phy *phy; + +static int usb_power_on(struct platform_device *pdev) +{ + if (IS_ERR(phy)) + return PTR_ERR(phy); + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + usb_phy_init(phy); + + return 0; +} + +static void usb_power_off(struct platform_device *pdev) +{ + if (IS_ERR(phy)) + return; + + usb_phy_shutdown(phy); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +} + +static int ehci_init_internal_buffer(struct usb_hcd *hcd) +{ + /* + * Below are recommended values from the datasheet; + * see [USB :: Setting of EHCI Internal Buffer]. + */ + /* EHCI IP internal buffer setting */ + iowrite32(0x00ff0040, hcd->regs + 0x0094); + /* EHCI IP internal buffer enable */ + iowrite32(0x00000001, hcd->regs + 0x009C); + + return 0; +} + +static struct usb_ehci_pdata ehcix_pdata = { + .power_on = usb_power_on, + .power_off = usb_power_off, + .power_suspend = usb_power_off, + .pre_setup = ehci_init_internal_buffer, +}; + +static struct resource ehci0_resources[] = { + [0] = { + .start = 0xffe70000, + .end = 0xffe70400 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_iid(0x4c), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ehci0_device = { + .name = "ehci-platform", + .id = 0, + .dev = { + .dma_mask = &ehci0_device.dev.coherent_dma_mask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &ehcix_pdata, + }, + .num_resources = ARRAY_SIZE(ehci0_resources), + .resource = ehci0_resources, +}; + +static struct resource ehci1_resources[] = { + [0] = { + .start = 0xfff70000, + .end = 0xfff70400 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_iid(0x4d), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ehci1_device = { + .name = "ehci-platform", + .id = 1, + .dev = { + .dma_mask = &ehci1_device.dev.coherent_dma_mask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &ehcix_pdata, + }, + .num_resources = ARRAY_SIZE(ehci1_resources), + .resource = ehci1_resources, +}; + +static struct usb_ohci_pdata ohcix_pdata = { + .power_on = usb_power_on, + .power_off = usb_power_off, + .power_suspend = usb_power_off, +}; + +static struct resource ohci0_resources[] = { + [0] = { + .start = 0xffe70400, + .end = 0xffe70800 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_iid(0x4c), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci0_device = { + .name = "ohci-platform", + .id = 0, + .dev = { + .dma_mask = &ohci0_device.dev.coherent_dma_mask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &ohcix_pdata, + }, + .num_resources = ARRAY_SIZE(ohci0_resources), + .resource = ohci0_resources, +}; + +static struct resource ohci1_resources[] = { + [0] = { + .start = 0xfff70400, + .end = 0xfff70800 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_iid(0x4d), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci1_device = { + .name = "ohci-platform", + .id = 1, + .dev = { + .dma_mask = &ohci1_device.dev.coherent_dma_mask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &ohcix_pdata, + }, + .num_resources = ARRAY_SIZE(ohci1_resources), + .resource = ohci1_resources, +}; + +/* HPB-DMA */ + +/* Asynchronous mode register bits */ +#define HPB_DMAE_ASYNCMDR_ASMD43_MASK BIT(23) /* MMC1 */ +#define HPB_DMAE_ASYNCMDR_ASMD43_SINGLE BIT(23) /* MMC1 */ +#define HPB_DMAE_ASYNCMDR_ASMD43_MULTI 0 /* MMC1 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD43_MASK BIT(22) /* MMC1 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD43_BURST BIT(22) /* MMC1 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD43_NBURST 0 /* MMC1 */ +#define HPB_DMAE_ASYNCMDR_ASMD24_MASK BIT(21) /* MMC0 */ +#define HPB_DMAE_ASYNCMDR_ASMD24_SINGLE BIT(21) /* MMC0 */ +#define HPB_DMAE_ASYNCMDR_ASMD24_MULTI 0 /* MMC0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD24_MASK BIT(20) /* MMC0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD24_BURST BIT(20) /* MMC0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD24_NBURST 0 /* MMC0 */ +#define HPB_DMAE_ASYNCMDR_ASMD41_MASK BIT(19) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD41_SINGLE BIT(19) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD41_MULTI 0 /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD41_MASK BIT(18) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD41_BURST BIT(18) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD41_NBURST 0 /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD40_MASK BIT(17) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD40_SINGLE BIT(17) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD40_MULTI 0 /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD40_MASK BIT(16) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD40_BURST BIT(16) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD40_NBURST 0 /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD39_MASK BIT(15) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD39_SINGLE BIT(15) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD39_MULTI 0 /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD39_MASK BIT(14) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD39_BURST BIT(14) /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD39_NBURST 0 /* SDHI3 */ +#define HPB_DMAE_ASYNCMDR_ASMD27_MASK BIT(13) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD27_SINGLE BIT(13) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD27_MULTI 0 /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD27_MASK BIT(12) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD27_BURST BIT(12) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD27_NBURST 0 /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD26_MASK BIT(11) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD26_SINGLE BIT(11) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD26_MULTI 0 /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD26_MASK BIT(10) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD26_BURST BIT(10) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD26_NBURST 0 /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD25_MASK BIT(9) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD25_SINGLE BIT(9) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD25_MULTI 0 /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD25_MASK BIT(8) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD25_BURST BIT(8) /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD25_NBURST 0 /* SDHI2 */ +#define HPB_DMAE_ASYNCMDR_ASMD23_MASK BIT(7) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD23_SINGLE BIT(7) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD23_MULTI 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD23_MASK BIT(6) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD23_BURST BIT(6) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD23_NBURST 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD22_MASK BIT(5) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD22_SINGLE BIT(5) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD22_MULTI 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD22_MASK BIT(4) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD22_BURST BIT(4) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD22_NBURST 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD21_MASK BIT(3) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD21_SINGLE BIT(3) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD21_MULTI 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD21_MASK BIT(2) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD21_BURST BIT(2) /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD21_NBURST 0 /* SDHI0 */ +#define HPB_DMAE_ASYNCMDR_ASMD20_MASK BIT(1) /* SDHI1 */ +#define HPB_DMAE_ASYNCMDR_ASMD20_SINGLE BIT(1) /* SDHI1 */ +#define HPB_DMAE_ASYNCMDR_ASMD20_MULTI 0 /* SDHI1 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD20_MASK BIT(0) /* SDHI1 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD20_BURST BIT(0) /* SDHI1 */ +#define HPB_DMAE_ASYNCMDR_ASBTMD20_NBURST 0 /* SDHI1 */ + +static const struct hpb_dmae_slave_config hpb_dmae_slaves[] = { + { + .id = HPBDMA_SLAVE_SDHI0_TX, + .addr = 0xffe4c000 + 0x30, + .dcr = HPB_DMAE_DCR_SPDS_16BIT | + HPB_DMAE_DCR_DMDL | + HPB_DMAE_DCR_DPDS_16BIT, + .rstr = HPB_DMAE_ASYNCRSTR_ASRST21 | + HPB_DMAE_ASYNCRSTR_ASRST22 | + HPB_DMAE_ASYNCRSTR_ASRST23, + .mdr = HPB_DMAE_ASYNCMDR_ASMD21_SINGLE | + HPB_DMAE_ASYNCMDR_ASBTMD21_NBURST, + .mdm = HPB_DMAE_ASYNCMDR_ASMD21_MASK | + HPB_DMAE_ASYNCMDR_ASBTMD21_MASK, + .port = 0x0D0C, + .flags = HPB_DMAE_SET_ASYNC_RESET | HPB_DMAE_SET_ASYNC_MODE, + .dma_ch = 21, + }, { + .id = HPBDMA_SLAVE_SDHI0_RX, + .addr = 0xffe4c000 + 0x30, + .dcr = HPB_DMAE_DCR_SMDL | + HPB_DMAE_DCR_SPDS_16BIT | + HPB_DMAE_DCR_DPDS_16BIT, + .rstr = HPB_DMAE_ASYNCRSTR_ASRST21 | + HPB_DMAE_ASYNCRSTR_ASRST22 | + HPB_DMAE_ASYNCRSTR_ASRST23, + .mdr = HPB_DMAE_ASYNCMDR_ASMD22_SINGLE | + HPB_DMAE_ASYNCMDR_ASBTMD22_NBURST, + .mdm = HPB_DMAE_ASYNCMDR_ASMD22_MASK | + HPB_DMAE_ASYNCMDR_ASBTMD22_MASK, + .port = 0x0D0C, + .flags = HPB_DMAE_SET_ASYNC_RESET | HPB_DMAE_SET_ASYNC_MODE, + .dma_ch = 22, + }, +}; + +static const struct hpb_dmae_channel hpb_dmae_channels[] = { + HPB_DMAE_CHANNEL(0x93, HPBDMA_SLAVE_SDHI0_TX), /* ch. 21 */ + HPB_DMAE_CHANNEL(0x93, HPBDMA_SLAVE_SDHI0_RX), /* ch. 22 */ +}; + +static struct hpb_dmae_pdata dma_platform_data __initdata = { + .slaves = hpb_dmae_slaves, + .num_slaves = ARRAY_SIZE(hpb_dmae_slaves), + .channels = hpb_dmae_channels, + .num_channels = ARRAY_SIZE(hpb_dmae_channels), + .ts_shift = { + [XMIT_SZ_8BIT] = 0, + [XMIT_SZ_16BIT] = 1, + [XMIT_SZ_32BIT] = 2, + }, + .num_hw_channels = 44, +}; + +static struct resource hpb_dmae_resources[] __initdata = { + /* Channel registers */ + DEFINE_RES_MEM(0xffc08000, 0x1000), + /* Common registers */ + DEFINE_RES_MEM(0xffc09000, 0x170), + /* Asynchronous reset registers */ + DEFINE_RES_MEM(0xffc00300, 4), + /* Asynchronous mode registers */ + DEFINE_RES_MEM(0xffc00400, 4), + /* IRQ for DMA channels */ + DEFINE_RES_NAMED(gic_iid(0x8e), 12, NULL, IORESOURCE_IRQ), +}; + +static void __init r8a7779_register_hpb_dmae(void) +{ + platform_device_register_resndata(NULL, "hpb-dma-engine", + -1, hpb_dmae_resources, + ARRAY_SIZE(hpb_dmae_resources), + &dma_platform_data, + sizeof(dma_platform_data)); +} + +static struct platform_device *r8a7779_early_devices[] __initdata = { + &tmu0_device, +}; + +static struct platform_device *r8a7779_standard_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &i2c0_device, + &i2c1_device, + &i2c2_device, + &i2c3_device, + &sata_device, +}; + +void __init r8a7779_add_standard_devices(void) +{ +#ifdef CONFIG_CACHE_L2X0 + /* Shared attribute override enable, 64K*16way */ + l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); +#endif + r8a7779_pm_init(); + + r8a7779_init_pm_domains(); + + platform_add_devices(r8a7779_early_devices, + ARRAY_SIZE(r8a7779_early_devices)); + platform_add_devices(r8a7779_standard_devices, + ARRAY_SIZE(r8a7779_standard_devices)); + r8a7779_register_hpb_dmae(); +} + +void __init r8a7779_add_early_devices(void) +{ + early_platform_add_devices(r8a7779_early_devices, + ARRAY_SIZE(r8a7779_early_devices)); + + /* Early serial console setup is not included here due to + * memory map collisions. The SCIF serial ports in r8a7779 + * are difficult to identity map 1:1 due to collision with the + * virtual memory range used by the coherent DMA code on ARM. + * + * Anyone wanting to debug early can remove UPF_IOREMAP from + * the sh-sci serial console platform data, adjust mapbase + * to a static M:N virt:phys mapping that needs to be added to + * the mappings passed with iotable_init() above. + * + * Then add a call to shmobile_setup_console() from this function. + * + * As a final step pass earlyprint=sh-sci.2,115200 on the kernel + * command line in case of the marzen board. + */ +} + +static struct platform_device *r8a7779_late_devices[] __initdata = { + &ehci0_device, + &ehci1_device, + &ohci0_device, + &ohci1_device, +}; + +void __init r8a7779_init_late(void) +{ + /* get USB PHY */ + phy = usb_get_phy(USB_PHY_TYPE_USB2); + + shmobile_init_late(); + platform_add_devices(r8a7779_late_devices, + ARRAY_SIZE(r8a7779_late_devices)); +} + +#ifdef CONFIG_USE_OF +void __init r8a7779_init_irq_dt(void) +{ +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + void __iomem *gic_dist_base = ioremap_nocache(0xf0001000, 0x1000); + void __iomem *gic_cpu_base = ioremap_nocache(0xf0000100, 0x1000); +#endif + gic_set_irqchip_flags(IRQCHIP_SKIP_SET_WAKE); + +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + gic_init(0, 29, gic_dist_base, gic_cpu_base); +#else + irqchip_init(); +#endif + /* route all interrupts to ARM */ + __raw_writel(0xffffffff, INT2NTSR0); + __raw_writel(0x3fffffff, INT2NTSR1); + + /* unmask all known interrupts in INTCS2 */ + __raw_writel(0xfffffff0, INT2SMSKCR0); + __raw_writel(0xfff7ffff, INT2SMSKCR1); + __raw_writel(0xfffbffdf, INT2SMSKCR2); + __raw_writel(0xbffffffc, INT2SMSKCR3); + __raw_writel(0x003fee3f, INT2SMSKCR4); +} + +#define MODEMR 0xffcc0020 + +u32 __init r8a7779_read_mode_pins(void) +{ + static u32 mode; + static bool mode_valid; + + if (!mode_valid) { + void __iomem *modemr = ioremap_nocache(MODEMR, PAGE_SIZE); + BUG_ON(!modemr); + mode = ioread32(modemr); + iounmap(modemr); + mode_valid = true; + } + + return mode; +} + +static const char *r8a7779_compat_dt[] __initdata = { + "renesas,r8a7779", + NULL, +}; + +DT_MACHINE_START(R8A7779_DT, "Generic R8A7779 (Flattened Device Tree)") + .map_io = r8a7779_map_io, + .init_early = shmobile_init_delay, + .init_irq = r8a7779_init_irq_dt, + .init_late = shmobile_init_late, + .dt_compat = r8a7779_compat_dt, +MACHINE_END +#endif /* CONFIG_USE_OF */ diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c b/arch/arm/mach-shmobile/setup-r8a7790.c new file mode 100644 index 000000000..3a18af492 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7790.c @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#include <linux/init.h> + +#include <asm/mach/arch.h> + +#include "common.h" +#include "r8a7790.h" +#include "rcar-gen2.h" + +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 = shmobile_init_delay, + .init_time = rcar_gen2_timer_init, + .init_late = shmobile_init_late, + .reserve = rcar_gen2_reserve, + .dt_compat = r8a7790_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a7791.c b/arch/arm/mach-shmobile/setup-r8a7791.c new file mode 100644 index 000000000..ef8eb3af5 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7791.c @@ -0,0 +1,38 @@ +/* + * r8a7791 processor support + * + * Copyright (C) 2013 Renesas Electronics Corporation + * 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. + */ + +#include <linux/init.h> + +#include <asm/mach/arch.h> + +#include "common.h" +#include "r8a7791.h" +#include "rcar-gen2.h" + +static const char *r8a7791_boards_compat_dt[] __initdata = { + "renesas,r8a7791", + NULL, +}; + +DT_MACHINE_START(R8A7791_DT, "Generic R8A7791 (Flattened Device Tree)") + .smp = smp_ops(r8a7791_smp_ops), + .init_early = shmobile_init_delay, + .init_time = rcar_gen2_timer_init, + .init_late = shmobile_init_late, + .reserve = rcar_gen2_reserve, + .dt_compat = r8a7791_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a7794.c b/arch/arm/mach-shmobile/setup-r8a7794.c new file mode 100644 index 000000000..d2b093033 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7794.c @@ -0,0 +1,33 @@ +/* + * r8a7794 processor support + * + * Copyright (C) 2014 Renesas Electronics Corporation + * Copyright (C) 2014 Ulrich Hecht + * + * 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. + */ + +#include <linux/of_platform.h> +#include "common.h" +#include "rcar-gen2.h" +#include <asm/mach/arch.h> + +static const char * const r8a7794_boards_compat_dt[] __initconst = { + "renesas,r8a7794", + NULL, +}; + +DT_MACHINE_START(R8A7794_DT, "Generic R8A7794 (Flattened Device Tree)") + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .init_time = rcar_gen2_timer_init, + .reserve = rcar_gen2_reserve, + .dt_compat = r8a7794_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-rcar-gen2.c b/arch/arm/mach-shmobile/setup-rcar-gen2.c new file mode 100644 index 000000000..5d13595aa --- /dev/null +++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c @@ -0,0 +1,203 @@ +/* + * R-Car Generation 2 support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Magnus Damm + * Copyright (C) 2014 Ulrich Hecht + * + * 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. + */ + +#include <linux/clk/shmobile.h> +#include <linux/clocksource.h> +#include <linux/device.h> +#include <linux/dma-contiguous.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/memblock.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <asm/mach/arch.h> +#include "common.h" +#include "rcar-gen2.h" + +#define MODEMR 0xe6160060 + +u32 rcar_gen2_read_mode_pins(void) +{ + static u32 mode; + static bool mode_valid; + + if (!mode_valid) { + void __iomem *modemr = ioremap_nocache(MODEMR, 4); + BUG_ON(!modemr); + mode = ioread32(modemr); + iounmap(modemr); + mode_valid = true; + } + + return mode; +} + +#define CNTCR 0 +#define CNTFID0 0x20 + +void __init rcar_gen2_timer_init(void) +{ + u32 mode = rcar_gen2_read_mode_pins(); +#ifdef CONFIG_ARM_ARCH_TIMER + void __iomem *base; + int extal_mhz = 0; + u32 freq; + + if (of_machine_is_compatible("renesas,r8a7794")) { + freq = 260000000 / 8; /* ZS / 8 */ + /* CNTVOFF has to be initialized either from non-secure + * Hypervisor mode or secure Monitor mode with SCR.NS==1. + * If TrustZone is enabled then it should be handled by the + * secure code. + */ + asm volatile( + " cps 0x16\n" + " mrc p15, 0, r1, c1, c1, 0\n" + " orr r0, r1, #1\n" + " mcr p15, 0, r0, c1, c1, 0\n" + " isb\n" + " mov r0, #0\n" + " mcrr p15, 4, r0, r0, c14\n" + " isb\n" + " mcr p15, 0, r1, c1, c1, 0\n" + " isb\n" + " cps 0x13\n" + : : : "r0", "r1"); + } else { + /* 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 the timer if it is either not running, or is not at the + * right frequency. The timer is only configurable in secure mode + * so this avoids an abort if the loader started the timer and + * entered the kernel in non-secure mode. + */ + + if ((ioread32(base + CNTCR) & 1) == 0 || + ioread32(base + CNTFID0) != freq) { + /* 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 */ + + rcar_gen2_clocks_init(mode); +#ifdef CONFIG_ARCH_SHMOBILE_MULTI + clocksource_of_init(); +#endif +} + +struct memory_reserve_config { + u64 reserved; + u64 base, size; +}; + +static int __init rcar_gen2_scan_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *reg, *endp; + int l; + struct memory_reserve_config *mrc = data; + u64 lpae_start = 1ULL << 32; + + /* We are scanning "memory" nodes only */ + if (type == NULL || strcmp(type, "memory")) + return 0; + + reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); + if (reg == NULL) + reg = of_get_flat_dt_prop(node, "reg", &l); + if (reg == NULL) + return 0; + + endp = reg + (l / sizeof(__be32)); + while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { + u64 base, size; + + base = dt_mem_next_cell(dt_root_addr_cells, ®); + size = dt_mem_next_cell(dt_root_size_cells, ®); + + if (base >= lpae_start) + continue; + + if ((base + size) >= lpae_start) + size = lpae_start - base; + + if (size < mrc->reserved) + continue; + + if (base < mrc->base) + continue; + + /* keep the area at top near the 32-bit legacy limit */ + mrc->base = base + size - mrc->reserved; + mrc->size = mrc->reserved; + } + + return 0; +} + +struct cma *rcar_gen2_dma_contiguous; + +void __init rcar_gen2_reserve(void) +{ + struct memory_reserve_config mrc; + + /* reserve 256 MiB at the top of the physical legacy 32-bit space */ + memset(&mrc, 0, sizeof(mrc)); + mrc.reserved = SZ_256M; + + of_scan_flat_dt(rcar_gen2_scan_mem, &mrc); +#ifdef CONFIG_DMA_CMA + if (mrc.size && memblock_is_region_memory(mrc.base, mrc.size)) + dma_contiguous_reserve_area(mrc.size, mrc.base, 0, + &rcar_gen2_dma_contiguous, true); +#endif +} diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c new file mode 100644 index 000000000..fb2ab7590 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -0,0 +1,810 @@ +/* + * sh73a0 processor support + * + * Copyright (C) 2010 Takashi Yoshii + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2008 Yoshihiro Shimoda + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/i2c/i2c-sh_mobile.h> +#include <linux/io.h> +#include <linux/serial_sci.h> +#include <linux/sh_dma.h> +#include <linux/sh_timer.h> +#include <linux/platform_data/sh_ipmmu.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> + +#include <asm/hardware/cache-l2x0.h> +#include <asm/mach-types.h> +#include <asm/mach/map.h> +#include <asm/mach/arch.h> +#include <asm/mach/time.h> + +#include "common.h" +#include "dma-register.h" +#include "intc.h" +#include "irqs.h" +#include "sh73a0.h" + +static struct map_desc sh73a0_io_desc[] __initdata = { + /* create a 1:1 identity mapping for 0xe6xxxxxx + * used by CPGA, INTC and PFC. + */ + { + .virtual = 0xe6000000, + .pfn = __phys_to_pfn(0xe6000000), + .length = 256 << 20, + .type = MT_DEVICE_NONSHARED + }, +}; + +void __init sh73a0_map_io(void) +{ + debug_ll_io_init(); + iotable_init(sh73a0_io_desc, ARRAY_SIZE(sh73a0_io_desc)); +} + +/* PFC */ +static struct resource pfc_resources[] __initdata = { + DEFINE_RES_MEM(0xe6050000, 0x8000), + DEFINE_RES_MEM(0xe605801c, 0x000c), +}; + +void __init sh73a0_pinmux_init(void) +{ + platform_device_register_simple("pfc-sh73a0", -1, pfc_resources, + ARRAY_SIZE(pfc_resources)); +} + +/* SCIF */ +#define SH73A0_SCIF(scif_type, index, baseaddr, irq) \ +static struct plat_sci_port scif##index##_platform_data = { \ + .type = scif_type, \ + .flags = UPF_BOOT_AUTOCONF, \ + .scscr = SCSCR_RE | SCSCR_TE, \ +}; \ + \ +static struct resource scif##index##_resources[] = { \ + DEFINE_RES_MEM(baseaddr, 0x100), \ + DEFINE_RES_IRQ(irq), \ +}; \ + \ +static struct platform_device scif##index##_device = { \ + .name = "sh-sci", \ + .id = index, \ + .resource = scif##index##_resources, \ + .num_resources = ARRAY_SIZE(scif##index##_resources), \ + .dev = { \ + .platform_data = &scif##index##_platform_data, \ + }, \ +} + +SH73A0_SCIF(PORT_SCIFA, 0, 0xe6c40000, gic_spi(72)); +SH73A0_SCIF(PORT_SCIFA, 1, 0xe6c50000, gic_spi(73)); +SH73A0_SCIF(PORT_SCIFA, 2, 0xe6c60000, gic_spi(74)); +SH73A0_SCIF(PORT_SCIFA, 3, 0xe6c70000, gic_spi(75)); +SH73A0_SCIF(PORT_SCIFA, 4, 0xe6c80000, gic_spi(78)); +SH73A0_SCIF(PORT_SCIFA, 5, 0xe6cb0000, gic_spi(79)); +SH73A0_SCIF(PORT_SCIFA, 6, 0xe6cc0000, gic_spi(156)); +SH73A0_SCIF(PORT_SCIFA, 7, 0xe6cd0000, gic_spi(143)); +SH73A0_SCIF(PORT_SCIFB, 8, 0xe6c30000, gic_spi(80)); + +static struct sh_timer_config cmt1_platform_data = { + .channels_mask = 0x3f, +}; + +static struct resource cmt1_resources[] = { + DEFINE_RES_MEM(0xe6138000, 0x200), + DEFINE_RES_IRQ(gic_spi(65)), +}; + +static struct platform_device cmt1_device = { + .name = "sh-cmt-48", + .id = 1, + .dev = { + .platform_data = &cmt1_platform_data, + }, + .resource = cmt1_resources, + .num_resources = ARRAY_SIZE(cmt1_resources), +}; + +/* TMU */ +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xfff60000, 0x2c), + DEFINE_RES_IRQ(intcs_evt2irq(0xe80)), + DEFINE_RES_IRQ(intcs_evt2irq(0xea0)), + DEFINE_RES_IRQ(intcs_evt2irq(0xec0)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct resource i2c0_resources[] = { + [0] = DEFINE_RES_MEM(0xe6820000, 0x426), + [1] = { + .start = gic_spi(167), + .end = gic_spi(170), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource i2c1_resources[] = { + [0] = DEFINE_RES_MEM(0xe6822000, 0x426), + [1] = { + .start = gic_spi(51), + .end = gic_spi(54), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource i2c2_resources[] = { + [0] = DEFINE_RES_MEM(0xe6824000, 0x426), + [1] = { + .start = gic_spi(171), + .end = gic_spi(174), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource i2c3_resources[] = { + [0] = DEFINE_RES_MEM(0xe6826000, 0x426), + [1] = { + .start = gic_spi(183), + .end = gic_spi(186), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource i2c4_resources[] = { + [0] = DEFINE_RES_MEM(0xe6828000, 0x426), + [1] = { + .start = gic_spi(187), + .end = gic_spi(190), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct i2c_sh_mobile_platform_data i2c_platform_data = { + .clks_per_count = 2, +}; + +static struct platform_device i2c0_device = { + .name = "i2c-sh_mobile", + .id = 0, + .resource = i2c0_resources, + .num_resources = ARRAY_SIZE(i2c0_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, +}; + +static struct platform_device i2c1_device = { + .name = "i2c-sh_mobile", + .id = 1, + .resource = i2c1_resources, + .num_resources = ARRAY_SIZE(i2c1_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, +}; + +static struct platform_device i2c2_device = { + .name = "i2c-sh_mobile", + .id = 2, + .resource = i2c2_resources, + .num_resources = ARRAY_SIZE(i2c2_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, +}; + +static struct platform_device i2c3_device = { + .name = "i2c-sh_mobile", + .id = 3, + .resource = i2c3_resources, + .num_resources = ARRAY_SIZE(i2c3_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, +}; + +static struct platform_device i2c4_device = { + .name = "i2c-sh_mobile", + .id = 4, + .resource = i2c4_resources, + .num_resources = ARRAY_SIZE(i2c4_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, +}; + +static const struct sh_dmae_slave_config sh73a0_dmae_slaves[] = { + { + .slave_id = SHDMA_SLAVE_SCIF0_TX, + .addr = 0xe6c40020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x21, + }, { + .slave_id = SHDMA_SLAVE_SCIF0_RX, + .addr = 0xe6c40024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x22, + }, { + .slave_id = SHDMA_SLAVE_SCIF1_TX, + .addr = 0xe6c50020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x25, + }, { + .slave_id = SHDMA_SLAVE_SCIF1_RX, + .addr = 0xe6c50024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x26, + }, { + .slave_id = SHDMA_SLAVE_SCIF2_TX, + .addr = 0xe6c60020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x29, + }, { + .slave_id = SHDMA_SLAVE_SCIF2_RX, + .addr = 0xe6c60024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x2a, + }, { + .slave_id = SHDMA_SLAVE_SCIF3_TX, + .addr = 0xe6c70020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x2d, + }, { + .slave_id = SHDMA_SLAVE_SCIF3_RX, + .addr = 0xe6c70024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x2e, + }, { + .slave_id = SHDMA_SLAVE_SCIF4_TX, + .addr = 0xe6c80020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x39, + }, { + .slave_id = SHDMA_SLAVE_SCIF4_RX, + .addr = 0xe6c80024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x3a, + }, { + .slave_id = SHDMA_SLAVE_SCIF5_TX, + .addr = 0xe6cb0020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x35, + }, { + .slave_id = SHDMA_SLAVE_SCIF5_RX, + .addr = 0xe6cb0024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x36, + }, { + .slave_id = SHDMA_SLAVE_SCIF6_TX, + .addr = 0xe6cc0020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x1d, + }, { + .slave_id = SHDMA_SLAVE_SCIF6_RX, + .addr = 0xe6cc0024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x1e, + }, { + .slave_id = SHDMA_SLAVE_SCIF7_TX, + .addr = 0xe6cd0020, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x19, + }, { + .slave_id = SHDMA_SLAVE_SCIF7_RX, + .addr = 0xe6cd0024, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x1a, + }, { + .slave_id = SHDMA_SLAVE_SCIF8_TX, + .addr = 0xe6c30040, + .chcr = CHCR_TX(XMIT_SZ_8BIT), + .mid_rid = 0x3d, + }, { + .slave_id = SHDMA_SLAVE_SCIF8_RX, + .addr = 0xe6c30060, + .chcr = CHCR_RX(XMIT_SZ_8BIT), + .mid_rid = 0x3e, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_TX, + .addr = 0xee100030, + .chcr = CHCR_TX(XMIT_SZ_16BIT), + .mid_rid = 0xc1, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_RX, + .addr = 0xee100030, + .chcr = CHCR_RX(XMIT_SZ_16BIT), + .mid_rid = 0xc2, + }, { + .slave_id = SHDMA_SLAVE_SDHI1_TX, + .addr = 0xee120030, + .chcr = CHCR_TX(XMIT_SZ_16BIT), + .mid_rid = 0xc9, + }, { + .slave_id = SHDMA_SLAVE_SDHI1_RX, + .addr = 0xee120030, + .chcr = CHCR_RX(XMIT_SZ_16BIT), + .mid_rid = 0xca, + }, { + .slave_id = SHDMA_SLAVE_SDHI2_TX, + .addr = 0xee140030, + .chcr = CHCR_TX(XMIT_SZ_16BIT), + .mid_rid = 0xcd, + }, { + .slave_id = SHDMA_SLAVE_SDHI2_RX, + .addr = 0xee140030, + .chcr = CHCR_RX(XMIT_SZ_16BIT), + .mid_rid = 0xce, + }, { + .slave_id = SHDMA_SLAVE_MMCIF_TX, + .addr = 0xe6bd0034, + .chcr = CHCR_TX(XMIT_SZ_32BIT), + .mid_rid = 0xd1, + }, { + .slave_id = SHDMA_SLAVE_MMCIF_RX, + .addr = 0xe6bd0034, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0xd2, + }, +}; + +#define DMAE_CHANNEL(_offset) \ + { \ + .offset = _offset - 0x20, \ + .dmars = _offset - 0x20 + 0x40, \ + } + +static const struct sh_dmae_channel sh73a0_dmae_channels[] = { + DMAE_CHANNEL(0x8000), + DMAE_CHANNEL(0x8080), + DMAE_CHANNEL(0x8100), + DMAE_CHANNEL(0x8180), + DMAE_CHANNEL(0x8200), + DMAE_CHANNEL(0x8280), + DMAE_CHANNEL(0x8300), + DMAE_CHANNEL(0x8380), + DMAE_CHANNEL(0x8400), + DMAE_CHANNEL(0x8480), + DMAE_CHANNEL(0x8500), + DMAE_CHANNEL(0x8580), + DMAE_CHANNEL(0x8600), + DMAE_CHANNEL(0x8680), + DMAE_CHANNEL(0x8700), + DMAE_CHANNEL(0x8780), + DMAE_CHANNEL(0x8800), + DMAE_CHANNEL(0x8880), + DMAE_CHANNEL(0x8900), + DMAE_CHANNEL(0x8980), +}; + +static struct sh_dmae_pdata sh73a0_dmae_platform_data = { + .slave = sh73a0_dmae_slaves, + .slave_num = ARRAY_SIZE(sh73a0_dmae_slaves), + .channel = sh73a0_dmae_channels, + .channel_num = ARRAY_SIZE(sh73a0_dmae_channels), + .ts_low_shift = TS_LOW_SHIFT, + .ts_low_mask = TS_LOW_BIT << TS_LOW_SHIFT, + .ts_high_shift = TS_HI_SHIFT, + .ts_high_mask = TS_HI_BIT << TS_HI_SHIFT, + .ts_shift = dma_ts_shift, + .ts_shift_num = ARRAY_SIZE(dma_ts_shift), + .dmaor_init = DMAOR_DME, +}; + +static struct resource sh73a0_dmae_resources[] = { + DEFINE_RES_MEM(0xfe000020, 0x89e0), + { + .name = "error_irq", + .start = gic_spi(129), + .end = gic_spi(129), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-19 */ + .start = gic_spi(109), + .end = gic_spi(128), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = sh73a0_dmae_resources, + .num_resources = ARRAY_SIZE(sh73a0_dmae_resources), + .dev = { + .platform_data = &sh73a0_dmae_platform_data, + }, +}; + +/* MPDMAC */ +static const struct sh_dmae_slave_config sh73a0_mpdma_slaves[] = { + { + .slave_id = SHDMA_SLAVE_FSI2A_RX, + .addr = 0xec230020, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0xd6, /* CHECK ME */ + }, { + .slave_id = SHDMA_SLAVE_FSI2A_TX, + .addr = 0xec230024, + .chcr = CHCR_TX(XMIT_SZ_32BIT), + .mid_rid = 0xd5, /* CHECK ME */ + }, { + .slave_id = SHDMA_SLAVE_FSI2C_RX, + .addr = 0xec230060, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0xda, /* CHECK ME */ + }, { + .slave_id = SHDMA_SLAVE_FSI2C_TX, + .addr = 0xec230064, + .chcr = CHCR_TX(XMIT_SZ_32BIT), + .mid_rid = 0xd9, /* CHECK ME */ + }, { + .slave_id = SHDMA_SLAVE_FSI2B_RX, + .addr = 0xec240020, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0x8e, /* CHECK ME */ + }, { + .slave_id = SHDMA_SLAVE_FSI2B_TX, + .addr = 0xec240024, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0x8d, /* CHECK ME */ + }, { + .slave_id = SHDMA_SLAVE_FSI2D_RX, + .addr = 0xec240060, + .chcr = CHCR_RX(XMIT_SZ_32BIT), + .mid_rid = 0x9a, /* CHECK ME */ + }, +}; + +#define MPDMA_CHANNEL(a, b, c) \ +{ \ + .offset = a, \ + .dmars = b, \ + .dmars_bit = c, \ + .chclr_offset = (0x220 - 0x20) + a \ +} + +static const struct sh_dmae_channel sh73a0_mpdma_channels[] = { + MPDMA_CHANNEL(0x00, 0, 0), + MPDMA_CHANNEL(0x10, 0, 8), + MPDMA_CHANNEL(0x20, 4, 0), + MPDMA_CHANNEL(0x30, 4, 8), + MPDMA_CHANNEL(0x50, 8, 0), + MPDMA_CHANNEL(0x70, 8, 8), +}; + +static struct sh_dmae_pdata sh73a0_mpdma_platform_data = { + .slave = sh73a0_mpdma_slaves, + .slave_num = ARRAY_SIZE(sh73a0_mpdma_slaves), + .channel = sh73a0_mpdma_channels, + .channel_num = ARRAY_SIZE(sh73a0_mpdma_channels), + .ts_low_shift = TS_LOW_SHIFT, + .ts_low_mask = TS_LOW_BIT << TS_LOW_SHIFT, + .ts_high_shift = TS_HI_SHIFT, + .ts_high_mask = TS_HI_BIT << TS_HI_SHIFT, + .ts_shift = dma_ts_shift, + .ts_shift_num = ARRAY_SIZE(dma_ts_shift), + .dmaor_init = DMAOR_DME, + .chclr_present = 1, +}; + +/* Resource order important! */ +static struct resource sh73a0_mpdma_resources[] = { + /* Channel registers and DMAOR */ + DEFINE_RES_MEM(0xec618020, 0x270), + /* DMARSx */ + DEFINE_RES_MEM(0xec619000, 0xc), + { + .name = "error_irq", + .start = gic_spi(181), + .end = gic_spi(181), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-5 */ + .start = gic_spi(175), + .end = gic_spi(180), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mpdma0_device = { + .name = "sh-dma-engine", + .id = 1, + .resource = sh73a0_mpdma_resources, + .num_resources = ARRAY_SIZE(sh73a0_mpdma_resources), + .dev = { + .platform_data = &sh73a0_mpdma_platform_data, + }, +}; + +static struct resource pmu_resources[] = { + [0] = { + .start = gic_spi(55), + .end = gic_spi(55), + .flags = IORESOURCE_IRQ, + }, + [1] = { + .start = gic_spi(56), + .end = gic_spi(56), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device pmu_device = { + .name = "armv7-pmu", + .id = -1, + .num_resources = ARRAY_SIZE(pmu_resources), + .resource = pmu_resources, +}; + +/* an IPMMU module for ICB */ +static struct resource ipmmu_resources[] = { + DEFINE_RES_MEM(0xfe951000, 0x100), +}; + +static const char * const ipmmu_dev_names[] = { + "sh_mobile_lcdc_fb.0", +}; + +static struct shmobile_ipmmu_platform_data ipmmu_platform_data = { + .dev_names = ipmmu_dev_names, + .num_dev_names = ARRAY_SIZE(ipmmu_dev_names), +}; + +static struct platform_device ipmmu_device = { + .name = "ipmmu", + .id = -1, + .dev = { + .platform_data = &ipmmu_platform_data, + }, + .resource = ipmmu_resources, + .num_resources = ARRAY_SIZE(ipmmu_resources), +}; + +static struct renesas_intc_irqpin_config irqpin0_platform_data = { + .irq_base = irq_pin(0), /* IRQ0 -> IRQ7 */ + .control_parent = true, +}; + +static struct resource irqpin0_resources[] = { + DEFINE_RES_MEM(0xe6900000, 4), /* ICR1A */ + DEFINE_RES_MEM(0xe6900010, 4), /* INTPRI00A */ + DEFINE_RES_MEM(0xe6900020, 1), /* INTREQ00A */ + DEFINE_RES_MEM(0xe6900040, 1), /* INTMSK00A */ + DEFINE_RES_MEM(0xe6900060, 1), /* INTMSKCLR00A */ + DEFINE_RES_IRQ(gic_spi(1)), /* IRQ0 */ + DEFINE_RES_IRQ(gic_spi(2)), /* IRQ1 */ + DEFINE_RES_IRQ(gic_spi(3)), /* IRQ2 */ + DEFINE_RES_IRQ(gic_spi(4)), /* IRQ3 */ + DEFINE_RES_IRQ(gic_spi(5)), /* IRQ4 */ + DEFINE_RES_IRQ(gic_spi(6)), /* IRQ5 */ + DEFINE_RES_IRQ(gic_spi(7)), /* IRQ6 */ + DEFINE_RES_IRQ(gic_spi(8)), /* IRQ7 */ +}; + +static struct platform_device irqpin0_device = { + .name = "renesas_intc_irqpin", + .id = 0, + .resource = irqpin0_resources, + .num_resources = ARRAY_SIZE(irqpin0_resources), + .dev = { + .platform_data = &irqpin0_platform_data, + }, +}; + +static struct renesas_intc_irqpin_config irqpin1_platform_data = { + .irq_base = irq_pin(8), /* IRQ8 -> IRQ15 */ + .control_parent = true, /* Disable spurious IRQ10 */ +}; + +static struct resource irqpin1_resources[] = { + DEFINE_RES_MEM(0xe6900004, 4), /* ICR2A */ + DEFINE_RES_MEM(0xe6900014, 4), /* INTPRI10A */ + DEFINE_RES_MEM(0xe6900024, 1), /* INTREQ10A */ + DEFINE_RES_MEM(0xe6900044, 1), /* INTMSK10A */ + DEFINE_RES_MEM(0xe6900064, 1), /* INTMSKCLR10A */ + DEFINE_RES_IRQ(gic_spi(9)), /* IRQ8 */ + DEFINE_RES_IRQ(gic_spi(10)), /* IRQ9 */ + DEFINE_RES_IRQ(gic_spi(11)), /* IRQ10 */ + DEFINE_RES_IRQ(gic_spi(12)), /* IRQ11 */ + DEFINE_RES_IRQ(gic_spi(13)), /* IRQ12 */ + DEFINE_RES_IRQ(gic_spi(14)), /* IRQ13 */ + DEFINE_RES_IRQ(gic_spi(15)), /* IRQ14 */ + DEFINE_RES_IRQ(gic_spi(16)), /* IRQ15 */ +}; + +static struct platform_device irqpin1_device = { + .name = "renesas_intc_irqpin", + .id = 1, + .resource = irqpin1_resources, + .num_resources = ARRAY_SIZE(irqpin1_resources), + .dev = { + .platform_data = &irqpin1_platform_data, + }, +}; + +static struct renesas_intc_irqpin_config irqpin2_platform_data = { + .irq_base = irq_pin(16), /* IRQ16 -> IRQ23 */ + .control_parent = true, +}; + +static struct resource irqpin2_resources[] = { + DEFINE_RES_MEM(0xe6900008, 4), /* ICR3A */ + DEFINE_RES_MEM(0xe6900018, 4), /* INTPRI20A */ + DEFINE_RES_MEM(0xe6900028, 1), /* INTREQ20A */ + DEFINE_RES_MEM(0xe6900048, 1), /* INTMSK20A */ + DEFINE_RES_MEM(0xe6900068, 1), /* INTMSKCLR20A */ + DEFINE_RES_IRQ(gic_spi(17)), /* IRQ16 */ + DEFINE_RES_IRQ(gic_spi(18)), /* IRQ17 */ + DEFINE_RES_IRQ(gic_spi(19)), /* IRQ18 */ + DEFINE_RES_IRQ(gic_spi(20)), /* IRQ19 */ + DEFINE_RES_IRQ(gic_spi(21)), /* IRQ20 */ + DEFINE_RES_IRQ(gic_spi(22)), /* IRQ21 */ + DEFINE_RES_IRQ(gic_spi(23)), /* IRQ22 */ + DEFINE_RES_IRQ(gic_spi(24)), /* IRQ23 */ +}; + +static struct platform_device irqpin2_device = { + .name = "renesas_intc_irqpin", + .id = 2, + .resource = irqpin2_resources, + .num_resources = ARRAY_SIZE(irqpin2_resources), + .dev = { + .platform_data = &irqpin2_platform_data, + }, +}; + +static struct renesas_intc_irqpin_config irqpin3_platform_data = { + .irq_base = irq_pin(24), /* IRQ24 -> IRQ31 */ + .control_parent = true, +}; + +static struct resource irqpin3_resources[] = { + DEFINE_RES_MEM(0xe690000c, 4), /* ICR4A */ + DEFINE_RES_MEM(0xe690001c, 4), /* INTPRI30A */ + DEFINE_RES_MEM(0xe690002c, 1), /* INTREQ30A */ + DEFINE_RES_MEM(0xe690004c, 1), /* INTMSK30A */ + DEFINE_RES_MEM(0xe690006c, 1), /* INTMSKCLR30A */ + DEFINE_RES_IRQ(gic_spi(25)), /* IRQ24 */ + DEFINE_RES_IRQ(gic_spi(26)), /* IRQ25 */ + DEFINE_RES_IRQ(gic_spi(27)), /* IRQ26 */ + DEFINE_RES_IRQ(gic_spi(28)), /* IRQ27 */ + DEFINE_RES_IRQ(gic_spi(29)), /* IRQ28 */ + DEFINE_RES_IRQ(gic_spi(30)), /* IRQ29 */ + DEFINE_RES_IRQ(gic_spi(31)), /* IRQ30 */ + DEFINE_RES_IRQ(gic_spi(32)), /* IRQ31 */ +}; + +static struct platform_device irqpin3_device = { + .name = "renesas_intc_irqpin", + .id = 3, + .resource = irqpin3_resources, + .num_resources = ARRAY_SIZE(irqpin3_resources), + .dev = { + .platform_data = &irqpin3_platform_data, + }, +}; + +static struct platform_device *sh73a0_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &scif8_device, + &tmu0_device, + &ipmmu_device, + &cmt1_device, +}; + +static struct platform_device *sh73a0_late_devices[] __initdata = { + &i2c0_device, + &i2c1_device, + &i2c2_device, + &i2c3_device, + &i2c4_device, + &dma0_device, + &mpdma0_device, + &pmu_device, + &irqpin0_device, + &irqpin1_device, + &irqpin2_device, + &irqpin3_device, +}; + +#define SRCR2 IOMEM(0xe61580b0) + +void __init sh73a0_add_standard_devices(void) +{ + /* Clear software reset bit on SY-DMAC module */ + __raw_writel(__raw_readl(SRCR2) & ~(1 << 18), SRCR2); + + platform_add_devices(sh73a0_early_devices, + ARRAY_SIZE(sh73a0_early_devices)); + platform_add_devices(sh73a0_late_devices, + ARRAY_SIZE(sh73a0_late_devices)); +} + +/* do nothing for !CONFIG_SMP or !CONFIG_HAVE_TWD */ +void __init __weak sh73a0_register_twd(void) { } + +void __init sh73a0_earlytimer_init(void) +{ + shmobile_init_delay(); +#ifndef CONFIG_COMMON_CLK + sh73a0_clock_init(); +#endif + shmobile_earlytimer_init(); + sh73a0_register_twd(); +} + +void __init sh73a0_add_early_devices(void) +{ + early_platform_add_devices(sh73a0_early_devices, + ARRAY_SIZE(sh73a0_early_devices)); + + /* setup early console here as well */ + shmobile_setup_console(); +} + +#ifdef CONFIG_USE_OF + +static void __init sh73a0_generic_init(void) +{ +#ifdef CONFIG_CACHE_L2X0 + /* Shared attribute override enable, 64K*8way */ + l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); +#endif + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *sh73a0_boards_compat_dt[] __initdata = { + "renesas,sh73a0", + NULL, +}; + +DT_MACHINE_START(SH73A0_DT, "Generic SH73A0 (Flattened Device Tree)") + .smp = smp_ops(sh73a0_smp_ops), + .map_io = sh73a0_map_io, + .init_early = shmobile_init_delay, + .init_machine = sh73a0_generic_init, + .init_late = shmobile_init_late, + .dt_compat = sh73a0_boards_compat_dt, +MACHINE_END +#endif /* CONFIG_USE_OF */ diff --git a/arch/arm/mach-shmobile/sh-gpio.h b/arch/arm/mach-shmobile/sh-gpio.h new file mode 100644 index 000000000..2c4141413 --- /dev/null +++ b/arch/arm/mach-shmobile/sh-gpio.h @@ -0,0 +1,29 @@ +/* + * Generic GPIO API and pinmux table support + * + * Copyright (c) 2008 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/io.h> + +/* + * FIXME !! + * + * current gpio frame work doesn't have + * the method to control only pull up/down/free. + * this function should be replaced by correct gpio function + */ +static inline void __init gpio_direction_none(void __iomem * addr) +{ + __raw_writeb(0x00, addr); +} + +#endif /* __ASM_ARCH_GPIO_H */ diff --git a/arch/arm/mach-shmobile/sh73a0.h b/arch/arm/mach-shmobile/sh73a0.h new file mode 100644 index 000000000..5a80f18b4 --- /dev/null +++ b/arch/arm/mach-shmobile/sh73a0.h @@ -0,0 +1,89 @@ +#ifndef __ASM_SH73A0_H__ +#define __ASM_SH73A0_H__ + +/* DMA slave IDs */ +enum { + SHDMA_SLAVE_INVALID, + SHDMA_SLAVE_SCIF0_TX, + SHDMA_SLAVE_SCIF0_RX, + SHDMA_SLAVE_SCIF1_TX, + SHDMA_SLAVE_SCIF1_RX, + SHDMA_SLAVE_SCIF2_TX, + SHDMA_SLAVE_SCIF2_RX, + SHDMA_SLAVE_SCIF3_TX, + SHDMA_SLAVE_SCIF3_RX, + SHDMA_SLAVE_SCIF4_TX, + SHDMA_SLAVE_SCIF4_RX, + SHDMA_SLAVE_SCIF5_TX, + SHDMA_SLAVE_SCIF5_RX, + SHDMA_SLAVE_SCIF6_TX, + SHDMA_SLAVE_SCIF6_RX, + SHDMA_SLAVE_SCIF7_TX, + SHDMA_SLAVE_SCIF7_RX, + SHDMA_SLAVE_SCIF8_TX, + SHDMA_SLAVE_SCIF8_RX, + SHDMA_SLAVE_SDHI0_TX, + SHDMA_SLAVE_SDHI0_RX, + SHDMA_SLAVE_SDHI1_TX, + SHDMA_SLAVE_SDHI1_RX, + SHDMA_SLAVE_SDHI2_TX, + SHDMA_SLAVE_SDHI2_RX, + SHDMA_SLAVE_MMCIF_TX, + SHDMA_SLAVE_MMCIF_RX, + SHDMA_SLAVE_FSI2A_TX, + SHDMA_SLAVE_FSI2A_RX, + SHDMA_SLAVE_FSI2B_TX, + SHDMA_SLAVE_FSI2B_RX, + SHDMA_SLAVE_FSI2C_TX, + SHDMA_SLAVE_FSI2C_RX, + SHDMA_SLAVE_FSI2D_RX, +}; + +/* + * SH73A0 IRQ LOCATION TABLE + * + * 416 ----------------------------------------- + * IRQ0-IRQ15 + * 431 ----------------------------------------- + * ... + * 448 ----------------------------------------- + * sh73a0-intcs + * sh73a0-intca-irq-pins + * 680 ----------------------------------------- + * ... + * 700 ----------------------------------------- + * sh73a0-pint0 + * 731 ----------------------------------------- + * 732 ----------------------------------------- + * sh73a0-pint1 + * 739 ----------------------------------------- + * ... + * 800 ----------------------------------------- + * IRQ16-IRQ31 + * 815 ----------------------------------------- + * ... + * 928 ----------------------------------------- + * sh73a0-intca-irq-pins + * 943 ----------------------------------------- + */ + +/* PINT interrupts are located at Linux IRQ 700 and up */ +#define SH73A0_PINT0_IRQ(irq) ((irq) + 700) +#define SH73A0_PINT1_IRQ(irq) ((irq) + 732) + +extern void sh73a0_init_irq(void); +extern void sh73a0_init_irq_dt(void); +extern void sh73a0_map_io(void); +extern void sh73a0_earlytimer_init(void); +extern void sh73a0_add_early_devices(void); +extern void sh73a0_add_standard_devices(void); +extern void sh73a0_clock_init(void); +extern void sh73a0_pinmux_init(void); +extern void sh73a0_pm_init(void); +extern struct clk sh73a0_extal1_clk; +extern struct clk sh73a0_extal2_clk; +extern struct clk sh73a0_extcki_clk; +extern struct clk sh73a0_extalr_clk; +extern struct smp_operations sh73a0_smp_ops; + +#endif /* __ASM_SH73A0_H__ */ diff --git a/arch/arm/mach-shmobile/smp-emev2.c b/arch/arm/mach-shmobile/smp-emev2.c new file mode 100644 index 000000000..baff3b5ef --- /dev/null +++ b/arch/arm/mach-shmobile/smp-emev2.c @@ -0,0 +1,55 @@ +/* + * SMP support for Emma Mobile EV2 + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include "common.h" + +#define EMEV2_SCU_BASE 0x1e000000 +#define EMEV2_SMU_BASE 0xe0110000 +#define SMU_GENERAL_REG0 0x7c0 + +static int emev2_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + arch_send_wakeup_ipi_mask(cpumask_of(cpu_logical_map(cpu))); + return 0; +} + +static void __init emev2_smp_prepare_cpus(unsigned int max_cpus) +{ + void __iomem *smu; + + /* Tell ROM loader about our vector (in headsmp.S) */ + smu = ioremap(EMEV2_SMU_BASE, PAGE_SIZE); + if (smu) { + iowrite32(__pa(shmobile_boot_vector), smu + SMU_GENERAL_REG0); + iounmap(smu); + } + + /* setup EMEV2 specific SCU bits */ + shmobile_scu_base = ioremap(EMEV2_SCU_BASE, PAGE_SIZE); + shmobile_smp_scu_prepare_cpus(max_cpus); +} + +struct smp_operations emev2_smp_ops __initdata = { + .smp_prepare_cpus = emev2_smp_prepare_cpus, + .smp_boot_secondary = emev2_boot_secondary, +}; diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c new file mode 100644 index 000000000..01f792fcb --- /dev/null +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -0,0 +1,136 @@ +/* + * SMP support for R-Mobile / SH-Mobile - r8a7779 portion + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include <asm/smp_twd.h> + +#include "common.h" +#include "pm-rcar.h" +#include "r8a7779.h" + +#define AVECR IOMEM(0xfe700040) +#define R8A7779_SCU_BASE 0xf0000000 + +static struct rcar_sysc_ch r8a7779_ch_cpu1 = { + .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */ + .chan_bit = 1, /* ARM1 */ + .isr_bit = 1, /* ARM1 */ +}; + +static struct rcar_sysc_ch r8a7779_ch_cpu2 = { + .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */ + .chan_bit = 2, /* ARM2 */ + .isr_bit = 2, /* ARM2 */ +}; + +static struct rcar_sysc_ch r8a7779_ch_cpu3 = { + .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */ + .chan_bit = 3, /* ARM3 */ + .isr_bit = 3, /* ARM3 */ +}; + +static struct rcar_sysc_ch *r8a7779_ch_cpu[4] = { + [1] = &r8a7779_ch_cpu1, + [2] = &r8a7779_ch_cpu2, + [3] = &r8a7779_ch_cpu3, +}; + +#if defined(CONFIG_HAVE_ARM_TWD) && !defined(CONFIG_ARCH_MULTIPLATFORM) +static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, R8A7779_SCU_BASE + 0x600, 29); +void __init r8a7779_register_twd(void) +{ + twd_local_timer_register(&twd_local_timer); +} +#endif + +static int r8a7779_platform_cpu_kill(unsigned int cpu) +{ + struct rcar_sysc_ch *ch = NULL; + int ret = -EIO; + + cpu = cpu_logical_map(cpu); + + if (cpu < ARRAY_SIZE(r8a7779_ch_cpu)) + ch = r8a7779_ch_cpu[cpu]; + + if (ch) + ret = rcar_sysc_power_down(ch); + + return ret ? ret : 1; +} + +static int r8a7779_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + struct rcar_sysc_ch *ch = NULL; + unsigned int lcpu = cpu_logical_map(cpu); + int ret; + + if (lcpu < ARRAY_SIZE(r8a7779_ch_cpu)) + ch = r8a7779_ch_cpu[lcpu]; + + if (ch) + ret = rcar_sysc_power_up(ch); + else + ret = -EIO; + + return ret; +} + +static void __init r8a7779_smp_prepare_cpus(unsigned int max_cpus) +{ + /* Map the reset vector (in headsmp-scu.S, headsmp.S) */ + __raw_writel(__pa(shmobile_boot_vector), AVECR); + shmobile_boot_fn = virt_to_phys(shmobile_boot_scu); + shmobile_boot_arg = (unsigned long)shmobile_scu_base; + + /* setup r8a7779 specific SCU bits */ + shmobile_scu_base = IOMEM(R8A7779_SCU_BASE); + shmobile_smp_scu_prepare_cpus(max_cpus); + + r8a7779_pm_init(); + + /* power off secondary CPUs */ + r8a7779_platform_cpu_kill(1); + r8a7779_platform_cpu_kill(2); + r8a7779_platform_cpu_kill(3); +} + +#ifdef CONFIG_HOTPLUG_CPU +static int r8a7779_cpu_kill(unsigned int cpu) +{ + if (shmobile_smp_scu_cpu_kill(cpu)) + return r8a7779_platform_cpu_kill(cpu); + + return 0; +} +#endif /* CONFIG_HOTPLUG_CPU */ + +struct smp_operations r8a7779_smp_ops __initdata = { + .smp_prepare_cpus = r8a7779_smp_prepare_cpus, + .smp_boot_secondary = r8a7779_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = shmobile_smp_scu_cpu_die, + .cpu_kill = r8a7779_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-shmobile/smp-r8a7790.c b/arch/arm/mach-shmobile/smp-r8a7790.c new file mode 100644 index 000000000..930f45cbc --- /dev/null +++ b/arch/arm/mach-shmobile/smp-r8a7790.c @@ -0,0 +1,71 @@ +/* + * SMP support for r8a7790 + * + * Copyright (C) 2012-2013 Renesas Solutions Corp. + * Copyright (C) 2012 Takashi Yoshii <takashi.yoshii.ze@renesas.com> + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/io.h> + +#include <asm/smp_plat.h> + +#include "common.h" +#include "platsmp-apmu.h" +#include "pm-rcar.h" +#include "rcar-gen2.h" +#include "r8a7790.h" + +static struct rcar_sysc_ch r8a7790_ca15_scu = { + .chan_offs = 0x180, /* PWRSR5 .. PWRER5 */ + .isr_bit = 12, /* CA15-SCU */ +}; + +static struct rcar_sysc_ch r8a7790_ca7_scu = { + .chan_offs = 0x100, /* PWRSR3 .. PWRER3 */ + .isr_bit = 21, /* CA7-SCU */ +}; + +static struct rcar_apmu_config r8a7790_apmu_config[] = { + { + .iomem = DEFINE_RES_MEM(0xe6152000, 0x188), + .cpus = { 0, 1, 2, 3 }, + }, + { + .iomem = DEFINE_RES_MEM(0xe6151000, 0x188), + .cpus = { 0x100, 0x0101, 0x102, 0x103 }, + } +}; + +static void __init r8a7790_smp_prepare_cpus(unsigned int max_cpus) +{ + /* let APMU code install data related to shmobile_boot_vector */ + shmobile_smp_apmu_prepare_cpus(max_cpus, + r8a7790_apmu_config, + ARRAY_SIZE(r8a7790_apmu_config)); + + /* turn on power to SCU */ + rcar_gen2_pm_init(); + rcar_sysc_power_up(&r8a7790_ca15_scu); + rcar_sysc_power_up(&r8a7790_ca7_scu); +} + +struct smp_operations r8a7790_smp_ops __initdata = { + .smp_prepare_cpus = r8a7790_smp_prepare_cpus, + .smp_boot_secondary = shmobile_smp_apmu_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = shmobile_smp_cpu_disable, + .cpu_die = shmobile_smp_apmu_cpu_die, + .cpu_kill = shmobile_smp_apmu_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-shmobile/smp-r8a7791.c b/arch/arm/mach-shmobile/smp-r8a7791.c new file mode 100644 index 000000000..5e2d1db79 --- /dev/null +++ b/arch/arm/mach-shmobile/smp-r8a7791.c @@ -0,0 +1,65 @@ +/* + * SMP support for r8a7791 + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/io.h> + +#include <asm/smp_plat.h> + +#include "common.h" +#include "platsmp-apmu.h" +#include "r8a7791.h" +#include "rcar-gen2.h" + +static struct rcar_apmu_config r8a7791_apmu_config[] = { + { + .iomem = DEFINE_RES_MEM(0xe6152000, 0x188), + .cpus = { 0, 1 }, + } +}; + +static void __init r8a7791_smp_prepare_cpus(unsigned int max_cpus) +{ + /* let APMU code install data related to shmobile_boot_vector */ + shmobile_smp_apmu_prepare_cpus(max_cpus, + r8a7791_apmu_config, + ARRAY_SIZE(r8a7791_apmu_config)); + + rcar_gen2_pm_init(); +} + +static int r8a7791_smp_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + /* Error out when hardware debug mode is enabled */ + if (rcar_gen2_read_mode_pins() & BIT(21)) { + pr_warn("Unable to boot CPU%u when MD21 is set\n", cpu); + return -ENOTSUPP; + } + + return shmobile_smp_apmu_boot_secondary(cpu, idle); +} + +struct smp_operations r8a7791_smp_ops __initdata = { + .smp_prepare_cpus = r8a7791_smp_prepare_cpus, + .smp_boot_secondary = r8a7791_smp_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = shmobile_smp_cpu_disable, + .cpu_die = shmobile_smp_apmu_cpu_die, + .cpu_kill = shmobile_smp_apmu_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c new file mode 100644 index 000000000..2106d6b76 --- /dev/null +++ b/arch/arm/mach-shmobile/smp-sh73a0.c @@ -0,0 +1,75 @@ +/* + * SMP support for R-Mobile / SH-Mobile - sh73a0 portion + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2010 Takashi Yoshii + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <asm/smp_plat.h> +#include <asm/smp_twd.h> + +#include "common.h" +#include "sh73a0.h" + +#define WUPCR IOMEM(0xe6151010) +#define SRESCR IOMEM(0xe6151018) +#define PSTR IOMEM(0xe6151040) +#define SBAR IOMEM(0xe6180020) +#define APARMBAREA IOMEM(0xe6f10020) + +#define SH73A0_SCU_BASE 0xf0000000 + +#if defined(CONFIG_HAVE_ARM_TWD) && !defined(CONFIG_ARCH_MULTIPLATFORM) +static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, SH73A0_SCU_BASE + 0x600, 29); +void __init sh73a0_register_twd(void) +{ + twd_local_timer_register(&twd_local_timer); +} +#endif + +static int sh73a0_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + unsigned int lcpu = cpu_logical_map(cpu); + + if (((__raw_readl(PSTR) >> (4 * lcpu)) & 3) == 3) + __raw_writel(1 << lcpu, WUPCR); /* wake up */ + else + __raw_writel(1 << lcpu, SRESCR); /* reset */ + + return 0; +} + +static void __init sh73a0_smp_prepare_cpus(unsigned int max_cpus) +{ + /* Map the reset vector (in headsmp.S) */ + __raw_writel(0, APARMBAREA); /* 4k */ + __raw_writel(__pa(shmobile_boot_vector), SBAR); + + /* setup sh73a0 specific SCU bits */ + shmobile_scu_base = IOMEM(SH73A0_SCU_BASE); + shmobile_smp_scu_prepare_cpus(max_cpus); +} + +struct smp_operations sh73a0_smp_ops __initdata = { + .smp_prepare_cpus = sh73a0_smp_prepare_cpus, + .smp_boot_secondary = sh73a0_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = shmobile_smp_cpu_disable, + .cpu_die = shmobile_smp_scu_cpu_die, + .cpu_kill = shmobile_smp_scu_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-shmobile/suspend.c b/arch/arm/mach-shmobile/suspend.c new file mode 100644 index 000000000..5d92b5dd4 --- /dev/null +++ b/arch/arm/mach-shmobile/suspend.c @@ -0,0 +1,48 @@ +/* + * Suspend-to-RAM support code for SH-Mobile ARM + * + * Copyright (C) 2011 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/cpu.h> + +#include <asm/io.h> +#include <asm/system_misc.h> + +static int shmobile_suspend_default_enter(suspend_state_t suspend_state) +{ + cpu_do_idle(); + return 0; +} + +static int shmobile_suspend_begin(suspend_state_t state) +{ + cpu_idle_poll_ctrl(true); + return 0; +} + +static void shmobile_suspend_end(void) +{ + cpu_idle_poll_ctrl(false); +} + +struct platform_suspend_ops shmobile_suspend_ops = { + .begin = shmobile_suspend_begin, + .end = shmobile_suspend_end, + .enter = shmobile_suspend_default_enter, + .valid = suspend_valid_only_mem, +}; + +int __init shmobile_suspend_init(void) +{ + suspend_set_ops(&shmobile_suspend_ops); + return 0; +} diff --git a/arch/arm/mach-shmobile/timer.c b/arch/arm/mach-shmobile/timer.c new file mode 100644 index 000000000..0edf2a6d2 --- /dev/null +++ b/arch/arm/mach-shmobile/timer.c @@ -0,0 +1,112 @@ +/* + * SH-Mobile Timer + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2002 - 2009 Paul Mundt + * + * 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. + */ +#include <linux/platform_device.h> +#include <linux/clocksource.h> +#include <linux/delay.h> +#include <linux/of_address.h> + +static void __init shmobile_setup_delay_hz(unsigned int max_cpu_core_hz, + unsigned int mult, unsigned int div) +{ + /* calculate a worst-case loops-per-jiffy value + * based on maximum cpu core hz setting and the + * __delay() implementation in arch/arm/lib/delay.S + * + * this will result in a longer delay than expected + * when the cpu core runs on lower frequencies. + */ + + unsigned int value = HZ * div / mult; + + if (!preset_lpj) + preset_lpj = max_cpu_core_hz / value; +} + +void __init shmobile_init_delay(void) +{ + struct device_node *np, *cpus; + bool is_a7_a8_a9 = false; + bool is_a15 = false; + bool has_arch_timer = false; + u32 max_freq = 0; + + cpus = of_find_node_by_path("/cpus"); + if (!cpus) + return; + + for_each_child_of_node(cpus, np) { + u32 freq; + + if (!of_property_read_u32(np, "clock-frequency", &freq)) + max_freq = max(max_freq, freq); + + if (of_device_is_compatible(np, "arm,cortex-a8") || + of_device_is_compatible(np, "arm,cortex-a9")) { + is_a7_a8_a9 = true; + } else if (of_device_is_compatible(np, "arm,cortex-a7")) { + is_a7_a8_a9 = true; + has_arch_timer = true; + } else if (of_device_is_compatible(np, "arm,cortex-a15")) { + is_a15 = true; + has_arch_timer = true; + } + } + + of_node_put(cpus); + + if (!max_freq) + return; + +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + /* Non-multiplatform r8a73a4 SoC cannot use arch timer due + * to GIC being initialized from C and arch timer via DT */ + if (of_machine_is_compatible("renesas,r8a73a4")) + has_arch_timer = false; + + /* Non-multiplatform r8a7790 SoC cannot use arch timer due + * to GIC being initialized from C and arch timer via DT */ + if (of_machine_is_compatible("renesas,r8a7790")) + has_arch_timer = false; +#endif + + if (!has_arch_timer || !IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) { + if (is_a7_a8_a9) + shmobile_setup_delay_hz(max_freq, 1, 3); + else if (is_a15) + shmobile_setup_delay_hz(max_freq, 2, 4); + } +} + +static void __init shmobile_late_time_init(void) +{ + /* + * Make sure all compiled-in early timers register themselves. + * + * Run probe() for two "earlytimer" devices, these will be the + * clockevents and clocksource devices respectively. In the event + * that only a clockevents device is available, we -ENODEV on the + * clocksource and the jiffies clocksource is used transparently + * instead. No error handling is necessary here. + */ + early_platform_driver_register_all("earlytimer"); + early_platform_driver_probe("earlytimer", 2, 0); +} + +void __init shmobile_earlytimer_init(void) +{ + late_time_init = shmobile_late_time_init; +} + |