ARM: SoC platform updates for v4.4

New and/or improved SoC support for this release:
 
  - Marvell Berlin:
    * Enable standard DT-based cpufreq
    * Add CPU hotplug support
  - Freescale:
    * Ethernet init for i.MX7D
    * Suspend/resume support for i.MX6UL
  - Allwinner:
    * Support for R8 chipset (used on NTC's $9 C.H.I.P board)
  - Mediatek:
    * SMP support for some platforms
  - Uniphier:
    * L2 support
    * Cleaned up SMP support, etc.
 
 + A handful of other patches around above functionality, and a few other
 smaller changes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJWQCmaAAoJEIwa5zzehBx3F/0QAKIYmvmJM3sUanNEEwhRilx+
 3xhSgld7e25suLGwrNapTkd8VzVB4b8GnJhNShNk+l5WqfqICHCB4Aru2NmJHY8V
 yPj5vBrgJTVMnIiH7CRDPz9IlwAkWM4MmWi4PgFuhrk1T/0wPKPNMc40OWOloTeD
 gA5YmbbX1hNOqKoI/z+DK7CEdp1lHrEjeYIbnQ0SldFzkY9NKhrI784gtcz3si6E
 19pFQ9LA7EtEv7aRcFOA0sazeooa2wiJ9P9L31Mn5APZBJj5H8HjyKdvOmJ8neQn
 +b77Tya11Q70U57uDq69l0rl58fpy650uTwYaLNGWmUdTgOiGMWN05lvIVNrQd/R
 gP+VEQDGsTH6kqOCy9gyLCmn9q9I7l0t9lwcu5TP52Xy9vqVq9rb388MkcPcsWk8
 cYPvD8RcSaywZUV3YJgbYozBfVuf5rLVus6D54pMXe3N12KGaNBt8kk4co4jBwvh
 b//1urA82cdlEAZ/kiqHXjRMq/ht+dxtb6sSVOJ9frxPLuc7g1z4ORC+Z0PTS5WC
 zB0hMzPnTwXeqHcYpV4wP/vGtgZGpLevBkK7pKVdqKZykV8BS4FiT4HFp6Rghxs3
 dxAz7JjQUle6KOX7YfuHdpLnyZFWQvLYyTn946xGKpw2QH/iWLwECpet2I87QzVs
 QkEkGygPhK4QcGccxz9N
 =h5xk
 -----END PGP SIGNATURE-----

Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC platform updates from Olof Johansson:
 "New and/or improved SoC support for this release:

  Marvell Berlin:
     - Enable standard DT-based cpufreq
     - Add CPU hotplug support

  Freescale:
     - Ethernet init for i.MX7D
     - Suspend/resume support for i.MX6UL

  Allwinner:
     - Support for R8 chipset (used on NTC's $9 C.H.I.P board)

  Mediatek:
     - SMP support for some platforms

  Uniphier:
     - L2 support
     - Cleaned up SMP support, etc.

  plus a handful of other patches around above functionality, and a few
  other smaller changes"

* tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (42 commits)
  ARM: uniphier: rework SMP operations to use trampoline code
  ARM: uniphier: add outer cache support
  Documentation: EXYNOS: Update bootloader interface on exynos542x
  ARM: mvebu: add broken-idle option
  ARM: orion5x: use mac_pton() helper
  ARM: at91: pm: at91_pm_suspend_in_sram() must be 8-byte aligned
  ARM: sunxi: Add R8 support
  ARM: digicolor: select pinctrl/gpio driver
  arm: berlin: add CPU hotplug support
  arm: berlin: use non-self-cleared reset register to reset cpu
  ARM: mediatek: add smp bringup code
  ARM: mediatek: enable gpt6 on boot up to make arch timer working
  soc: mediatek: Fix random hang up issue while kernel init
  soc: ti: qmss: make acc queue support optional in the driver
  soc: ti: add firmware file name as part of the driver
  Documentation: dt: soc: Add description for knav qmss driver
  ARM: S3C64XX: Use PWM lookup table for mach-smartq
  ARM: S3C64XX: Use PWM lookup table for mach-hmt
  ARM: S3C64XX: Use PWM lookup table for mach-crag6410
  ARM: S3C64XX: Use PWM lookup table for smdk6410
  ...
This commit is contained in:
Linus Torvalds 2015-11-10 14:56:23 -08:00
commit 56e0464980
70 changed files with 1888 additions and 296 deletions

View file

@ -123,29 +123,23 @@ choice
0x80020000 | 0xf0020000 | UART8
0x80024000 | 0xf0024000 | UART9
config AT91_DEBUG_LL_DBGU0
bool "Kernel low-level debugging on rm9200, 9260/9g20, 9261/9g10, 9rl, 9x5, 9n12"
select DEBUG_AT91_UART
config DEBUG_AT91_UART
bool "Kernel low-level debugging on Atmel SoCs"
depends on ARCH_AT91
depends on SOC_AT91RM9200 || SOC_AT91SAM9
help
Say Y here if you want the debug print routines to direct
their output to the serial port on atmel devices.
config AT91_DEBUG_LL_DBGU1
bool "Kernel low-level debugging on 9263, 9g45 and sama5d3"
select DEBUG_AT91_UART
depends on ARCH_AT91
depends on SOC_AT91SAM9 || SOC_SAMA5
SOC DEBUG_UART_PHYS DEBUG_UART_VIRT PORT
rm9200, 9260/9g20, 0xfffff200 0xfefff200 DBGU
9261/9g10, 9rl
9263, 9g45, sama5d3 0xffffee00 0xfeffee00 DBGU
sama5d4 0xfc00c000 0xfb00c000 USART3
sama5d4 0xfc069000 0xfb069000 DBGU
sama5d2 0xf8020000 0xf7020000 UART1
config AT91_DEBUG_LL_DBGU2
bool "Kernel low-level debugging on sama5d4"
select DEBUG_AT91_UART
depends on ARCH_AT91
depends on SOC_SAMA5
config AT91_DEBUG_LL_DBGU3
bool "Kernel low-level debugging on sama5d2"
select DEBUG_AT91_UART
depends on ARCH_AT91
depends on SOC_SAMA5
Please adjust DEBUG_UART_PHYS configuration options based on
your needs.
config DEBUG_BCM2835
bool "Kernel low-level debugging on BCM2835 PL011 UART"
@ -1249,10 +1243,6 @@ choice
endchoice
config DEBUG_AT91_UART
bool
depends on ARCH_AT91
config DEBUG_EXYNOS_UART
bool
@ -1485,7 +1475,8 @@ config DEBUG_UART_PHYS
DEBUG_RMOBILE_SCIFA0 || DEBUG_RMOBILE_SCIFA1 || \
DEBUG_RMOBILE_SCIFA4 || DEBUG_S3C24XX_UART || \
DEBUG_UART_BCM63XX || DEBUG_ASM9260_UART || \
DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0
DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \
DEBUG_AT91_UART
config DEBUG_UART_VIRT
hex "Virtual base address of debug UART"

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.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, 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.
*/
#ifndef __CACHE_UNIPHIER_H
#define __CACHE_UNIPHIER_H
#include <linux/types.h>
#ifdef CONFIG_CACHE_UNIPHIER
int uniphier_cache_init(void);
int uniphier_cache_l2_is_enabled(void);
void uniphier_cache_l2_touch_range(unsigned long start, unsigned long end);
void uniphier_cache_l2_set_locked_ways(u32 way_mask);
#else
static inline int uniphier_cache_init(void)
{
return -ENODEV;
}
static inline int uniphier_cache_l2_is_enabled(void)
{
return 0;
}
static inline void uniphier_cache_l2_touch_range(unsigned long start,
unsigned long end)
{
}
static inline void uniphier_cache_l2_set_locked_ways(u32 way_mask)
{
}
#endif
#endif /* __CACHE_UNIPHIER_H */

View file

@ -9,32 +9,22 @@
*
*/
#if defined(CONFIG_AT91_DEBUG_LL_DBGU0)
#define AT91_DBGU 0xfffff200 /* AT91_BASE_DBGU0 */
#elif defined(CONFIG_AT91_DEBUG_LL_DBGU1)
#define AT91_DBGU 0xffffee00 /* AT91_BASE_DBGU1 */
#elif defined(CONFIG_AT91_DEBUG_LL_DBGU2)
/* On sama5d4, use USART3 as low level serial console */
#define AT91_DBGU 0xfc00c000 /* SAMA5D4_BASE_USART3 */
#else
/* On sama5d2, use UART1 as low level serial console */
#define AT91_DBGU 0xf8020000
#endif
#ifdef CONFIG_MMU
#define AT91_IO_P2V(x) ((x) - 0x01000000)
#else
#define AT91_IO_P2V(x) (x)
#endif
#define CONFIG_DEBUG_UART_VIRT AT91_IO_P2V(CONFIG_DEBUG_UART_PHYS)
#define AT91_DBGU_SR (0x14) /* Status Register */
#define AT91_DBGU_THR (0x1c) /* Transmitter Holding Register */
#define AT91_DBGU_TXRDY (1 << 1) /* Transmitter Ready */
#define AT91_DBGU_TXEMPTY (1 << 9) /* Transmitter Empty */
.macro addruart, rp, rv, tmp
ldr \rp, =AT91_DBGU @ System peripherals (phys address)
ldr \rv, =AT91_IO_P2V(AT91_DBGU) @ System peripherals (virt address)
ldr \rp, =CONFIG_DEBUG_UART_PHYS @ System peripherals (phys address)
ldr \rv, =CONFIG_DEBUG_UART_VIRT @ System peripherals (virt address)
.endm
.macro senduart,rd,rx

View file

@ -39,6 +39,7 @@
#include <linux/export.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/cache-uniphier.h>
#include <asm/outercache.h>
#include <asm/exception.h>
#include <asm/mach/arch.h>
@ -97,6 +98,8 @@ void __init init_IRQ(void)
if (ret)
pr_err("L2C: failed to init: %d\n", ret);
}
uniphier_cache_init();
}
#ifdef CONFIG_MULTI_IRQ_HANDLER

View file

@ -80,6 +80,8 @@ tmp2 .req r5
* @r2: base address of second SDRAM Controller or 0 if not present
* @r3: pm information
*/
/* at91_pm_suspend_in_sram must be 8-byte aligned per the requirements of fncpy() */
.align 3
ENTRY(at91_pm_suspend_in_sram)
/* Save registers on stack */
stmfd sp!, {r4 - r12, lr}

View file

@ -35,6 +35,20 @@ config ARCH_BCM_CYGNUS
BCM11300, BCM11320, BCM11350, BCM11360,
BCM58300, BCM58302, BCM58303, BCM58305.
config ARCH_BCM_NSP
bool "Broadcom Northstar Plus SoC Support" if ARCH_MULTI_V7
select ARCH_BCM_IPROC
select ARM_ERRATA_754322
select ARM_ERRATA_775420
help
Support for Broadcom Northstar Plus SoC.
Broadcom Northstar Plus family of SoCs are used for switching control
and management applications as well as residential router/gateway
applications. The SoC features dual core Cortex A9 ARM CPUs,
integrating several peripheral interfaces including multiple Gigabit
Ethernet PHYs, DDR3 memory, PCIE Gen-2, USB 2.0 and USB 3.0, serial and
NAND flash, SATA and several other IO controllers.
config ARCH_BCM_5301X
bool "Broadcom BCM470X / BCM5301X ARM SoC" if ARCH_MULTI_V7
select ARCH_BCM_IPROC
@ -147,6 +161,7 @@ config ARCH_BRCMSTB
select BCM7120_L2_IRQ
select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
select ARCH_WANT_OPTIONAL_GPIOLIB
select SOC_BRCMSTB
help
Say Y if you intend to run the kernel on a Broadcom ARM-based STB
chipset.

View file

@ -1,5 +1,5 @@
#
# Copyright (C) 2012-2014 Broadcom Corporation
# Copyright (C) 2012-2015 Broadcom Corporation
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@ -13,6 +13,9 @@
# Cygnus
obj-$(CONFIG_ARCH_BCM_CYGNUS) += bcm_cygnus.o
# Northstar Plus
obj-$(CONFIG_ARCH_BCM_NSP) += bcm_nsp.o
# BCM281XX
obj-$(CONFIG_ARCH_BCM_281XX) += board_bcm281xx.o

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2015 Broadcom 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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <asm/mach/arch.h>
static const char *const bcm_nsp_dt_compat[] __initconst = {
"brcm,nsp",
NULL,
};
DT_MACHINE_START(NSP_DT, "Broadcom Northstar Plus SoC")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
.dt_compat = bcm_nsp_dt_compat,
MACHINE_END

View file

@ -12,11 +12,19 @@
*/
#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/of_platform.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
static void __init brcmstb_init_irq(void)
{
irqchip_init();
brcmstb_biuctrl_init();
}
static const char *const brcmstb_match[] __initconst = {
"brcm,bcm7445",
"brcm,brcmstb",
@ -25,4 +33,5 @@ static const char *const brcmstb_match[] __initconst = {
DT_MACHINE_START(BRCMSTB, "Broadcom STB (Flattened Device Tree)")
.dt_compat = brcmstb_match,
.init_irq = brcmstb_init_irq,
MACHINE_END

View file

@ -18,6 +18,11 @@
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
static void __init berlin_init_late(void)
{
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
}
static const char * const berlin_dt_compat[] = {
"marvell,berlin",
NULL,
@ -25,6 +30,7 @@ static const char * const berlin_dt_compat[] = {
DT_MACHINE_START(BERLIN_DT, "Marvell Berlin")
.dt_compat = berlin_dt_compat,
.init_late = berlin_init_late,
/*
* with DT probing for L2CCs, berlin_init_machine can be removed.
* Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc

View file

@ -14,10 +14,16 @@
#include <linux/of_address.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#define CPU_RESET 0x00
/*
* There are two reset registers, one with self-clearing (SC)
* reset and one with non-self-clearing reset (NON_SC).
*/
#define CPU_RESET_SC 0x00
#define CPU_RESET_NON_SC 0x20
#define RESET_VECT 0x00
#define SW_RESET_ADDR 0x94
@ -30,9 +36,11 @@ static inline void berlin_perform_reset_cpu(unsigned int cpu)
{
u32 val;
val = readl(cpu_ctrl + CPU_RESET);
val = readl(cpu_ctrl + CPU_RESET_NON_SC);
val &= ~BIT(cpu_logical_map(cpu));
writel(val, cpu_ctrl + CPU_RESET_NON_SC);
val |= BIT(cpu_logical_map(cpu));
writel(val, cpu_ctrl + CPU_RESET);
writel(val, cpu_ctrl + CPU_RESET_NON_SC);
}
static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
@ -91,8 +99,32 @@ unmap_scu:
iounmap(scu_base);
}
#ifdef CONFIG_HOTPLUG_CPU
static void berlin_cpu_die(unsigned int cpu)
{
v7_exit_coherency_flush(louis);
while (1)
cpu_do_idle();
}
static int berlin_cpu_kill(unsigned int cpu)
{
u32 val;
val = readl(cpu_ctrl + CPU_RESET_NON_SC);
val &= ~BIT(cpu_logical_map(cpu));
writel(val, cpu_ctrl + CPU_RESET_NON_SC);
return 1;
}
#endif
static struct smp_operations berlin_smp_ops __initdata = {
.smp_prepare_cpus = berlin_smp_prepare_cpus,
.smp_boot_secondary = berlin_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = berlin_cpu_die,
.cpu_kill = berlin_cpu_kill,
#endif
};
CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);

View file

@ -1,7 +1,10 @@
config ARCH_DIGICOLOR
bool "Conexant Digicolor SoC Support"
depends on ARCH_MULTI_V7
select ARCH_REQUIRE_GPIOLIB
select CLKSRC_MMIO
select DIGICOLOR_TIMER
select GENERIC_IRQ_CHIP
select MFD_SYSCON
select PINCTRL
select PINCTRL_DIGICOLOR

View file

@ -131,6 +131,7 @@ void imx6q_pm_init(void);
void imx6dl_pm_init(void);
void imx6sl_pm_init(void);
void imx6sx_pm_init(void);
void imx6ul_pm_init(void);
#ifdef CONFIG_PM
void imx51_pm_init(void);

View file

@ -67,6 +67,7 @@ static void __init imx6ul_init_machine(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
imx6ul_enet_init();
imx_anatop_init();
imx6ul_pm_init();
}
static void __init imx6ul_init_irq(void)
@ -74,6 +75,13 @@ static void __init imx6ul_init_irq(void)
imx_init_revision_from_anatop();
imx_src_init();
irqchip_init();
imx6_pm_ccm_init("fsl,imx6ul-ccm");
}
static void __init imx6ul_init_late(void)
{
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
}
static const char *imx6ul_dt_compat[] __initconst = {
@ -84,5 +92,6 @@ static const char *imx6ul_dt_compat[] __initconst = {
DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.init_late = imx6ul_init_late,
.dt_compat = imx6ul_dt_compat,
MACHINE_END

View file

@ -6,12 +6,85 @@
* published by the Free Software Foundation.
*/
#include <linux/irqchip.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/regmap.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "common.h"
static int ar8031_phy_fixup(struct phy_device *dev)
{
u16 val;
/* Set RGMII IO voltage to 1.8V */
phy_write(dev, 0x1d, 0x1f);
phy_write(dev, 0x1e, 0x8);
/* disable phy AR8031 SmartEEE function. */
phy_write(dev, 0xd, 0x3);
phy_write(dev, 0xe, 0x805d);
phy_write(dev, 0xd, 0x4003);
val = phy_read(dev, 0xe);
val &= ~(0x1 << 8);
phy_write(dev, 0xe, val);
/* introduce tx clock delay */
phy_write(dev, 0x1d, 0x5);
val = phy_read(dev, 0x1e);
val |= 0x0100;
phy_write(dev, 0x1e, val);
return 0;
}
static int bcm54220_phy_fixup(struct phy_device *dev)
{
/* enable RXC skew select RGMII copper mode */
phy_write(dev, 0x1e, 0x21);
phy_write(dev, 0x1f, 0x7ea8);
phy_write(dev, 0x1e, 0x2f);
phy_write(dev, 0x1f, 0x71b7);
return 0;
}
#define PHY_ID_AR8031 0x004dd074
#define PHY_ID_BCM54220 0x600d8589
static void __init imx7d_enet_phy_init(void)
{
if (IS_BUILTIN(CONFIG_PHYLIB)) {
phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff,
ar8031_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_BCM54220, 0xffffffff,
bcm54220_phy_fixup);
}
}
static void __init imx7d_enet_clk_sel(void)
{
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx7d-iomuxc-gpr");
if (!IS_ERR(gpr)) {
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_TX_CLK_SEL_MASK, 0);
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_CLK_DIR_MASK, 0);
} else {
pr_err("failed to find fsl,imx7d-iomux-gpr regmap\n");
}
}
static inline void imx7d_enet_init(void)
{
imx7d_enet_phy_init();
imx7d_enet_clk_sel();
}
static void __init imx7d_init_machine(void)
{
struct device *parent;
@ -22,6 +95,7 @@ static void __init imx7d_init_machine(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
imx_anatop_init();
imx7d_enet_init();
}
static void __init imx7d_init_irq(void)

View file

@ -93,6 +93,7 @@ struct imx6_pm_socdata {
const char *src_compat;
const char *iomuxc_compat;
const char *gpc_compat;
const char *pl310_compat;
const u32 mmdc_io_num;
const u32 *mmdc_io_offset;
};
@ -137,11 +138,19 @@ static const u32 imx6sx_mmdc_io_offset[] __initconst = {
0x330, 0x334, 0x338, 0x33c, /* SDQS0 ~ SDQS3 */
};
static const u32 imx6ul_mmdc_io_offset[] __initconst = {
0x244, 0x248, 0x24c, 0x250, /* DQM0, DQM1, RAS, CAS */
0x27c, 0x498, 0x4a4, 0x490, /* SDCLK0, GPR_B0DS-B1DS, GPR_ADDS */
0x280, 0x284, 0x260, 0x264, /* SDQS0~1, SODT0, SODT1 */
0x494, 0x4b0, /* MODE_CTL, MODE, */
};
static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
.mmdc_compat = "fsl,imx6q-mmdc",
.src_compat = "fsl,imx6q-src",
.iomuxc_compat = "fsl,imx6q-iomuxc",
.gpc_compat = "fsl,imx6q-gpc",
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
.mmdc_io_offset = imx6q_mmdc_io_offset,
};
@ -151,6 +160,7 @@ static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
.src_compat = "fsl,imx6q-src",
.iomuxc_compat = "fsl,imx6dl-iomuxc",
.gpc_compat = "fsl,imx6q-gpc",
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
.mmdc_io_offset = imx6dl_mmdc_io_offset,
};
@ -160,6 +170,7 @@ static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
.src_compat = "fsl,imx6sl-src",
.iomuxc_compat = "fsl,imx6sl-iomuxc",
.gpc_compat = "fsl,imx6sl-gpc",
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
.mmdc_io_offset = imx6sl_mmdc_io_offset,
};
@ -169,10 +180,21 @@ static const struct imx6_pm_socdata imx6sx_pm_data __initconst = {
.src_compat = "fsl,imx6sx-src",
.iomuxc_compat = "fsl,imx6sx-iomuxc",
.gpc_compat = "fsl,imx6sx-gpc",
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset),
.mmdc_io_offset = imx6sx_mmdc_io_offset,
};
static const struct imx6_pm_socdata imx6ul_pm_data __initconst = {
.mmdc_compat = "fsl,imx6ul-mmdc",
.src_compat = "fsl,imx6ul-src",
.iomuxc_compat = "fsl,imx6ul-iomuxc",
.gpc_compat = "fsl,imx6ul-gpc",
.pl310_compat = NULL,
.mmdc_io_num = ARRAY_SIZE(imx6ul_mmdc_io_offset),
.mmdc_io_offset = imx6ul_mmdc_io_offset,
};
/*
* This structure is for passing necessary data for low level ocram
* suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct
@ -290,7 +312,7 @@ int imx6_set_lpm(enum mxc_cpu_pwr_mode mode)
val |= BM_CLPCR_SBYOS;
if (cpu_is_imx6sl())
val |= BM_CLPCR_BYPASS_PMIC_READY;
if (cpu_is_imx6sl() || cpu_is_imx6sx())
if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
else
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
@ -330,6 +352,10 @@ static int imx6q_suspend_finish(unsigned long val)
* as we need to float DDR IO.
*/
local_flush_tlb_all();
/* check if need to flush internal L2 cache */
if (!((struct imx6_cpu_pm_info *)
suspend_ocram_base)->l2_base.vbase)
flush_cache_all();
imx6_suspend_in_ocram_fn(suspend_ocram_base);
}
@ -470,6 +496,7 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
MX6Q_SUSPEND_OCRAM_SIZE, false);
memset(suspend_ocram_base, 0, sizeof(*pm_info));
pm_info = suspend_ocram_base;
pm_info->pbase = ocram_pbase;
pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
@ -505,11 +532,13 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
goto gpc_map_failed;
}
ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache");
if (ret) {
pr_warn("%s: failed to get pl310-cache base %d!\n",
__func__, ret);
goto pl310_cache_map_failed;
if (socdata->pl310_compat) {
ret = imx6_pm_get_base(&pm_info->l2_base, socdata->pl310_compat);
if (ret) {
pr_warn("%s: failed to get pl310-cache base %d!\n",
__func__, ret);
goto pl310_cache_map_failed;
}
}
pm_info->ddr_type = imx_mmdc_get_ddr_type();
@ -610,3 +639,8 @@ void __init imx6sx_pm_init(void)
{
imx6_pm_common_init(&imx6sx_pm_data);
}
void __init imx6ul_pm_init(void)
{
imx6_pm_common_init(&imx6ul_pm_data);
}

View file

@ -79,12 +79,15 @@
/* sync L2 cache to drain L2's buffers to DRAM. */
#ifdef CONFIG_CACHE_L2X0
ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
teq r11, #0
beq 6f
mov r6, #0x0
str r6, [r11, #L2X0_CACHE_SYNC]
1:
ldr r6, [r11, #L2X0_CACHE_SYNC]
ands r6, r6, #0x1
bne 1b
6:
#endif
.endm

View file

@ -1 +1,4 @@
ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_ARCH_MEDIATEK) += platsmp.o
endif
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek.o

View file

@ -16,6 +16,32 @@
*/
#include <linux/init.h>
#include <asm/mach/arch.h>
#include <linux/of.h>
#include <linux/clk-provider.h>
#include <linux/clocksource.h>
#define GPT6_CON_MT65xx 0x10008060
#define GPT_ENABLE 0x31
static void __init mediatek_timer_init(void)
{
void __iomem *gpt_base;
if (of_machine_is_compatible("mediatek,mt6589") ||
of_machine_is_compatible("mediatek,mt8135") ||
of_machine_is_compatible("mediatek,mt8127")) {
/* turn on GPT6 which ungates arch timer clocks */
gpt_base = ioremap(GPT6_CON_MT65xx, 0x04);
/* enable clock and set to free-run */
writel(GPT_ENABLE, gpt_base);
iounmap(gpt_base);
}
of_clk_init(NULL);
clocksource_probe();
};
static const char * const mediatek_board_dt_compat[] = {
"mediatek,mt6589",
@ -27,4 +53,5 @@ static const char * const mediatek_board_dt_compat[] = {
DT_MACHINE_START(MEDIATEK_DT, "Mediatek Cortex-A7 (Device Tree)")
.dt_compat = mediatek_board_dt_compat,
.init_time = mediatek_timer_init,
MACHINE_END

View file

@ -0,0 +1,141 @@
/*
* arch/arm/mach-mediatek/platsmp.c
*
* Copyright (c) 2014 Mediatek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
* Yingjoe Chen <yingjoe.chen@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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/io.h>
#include <linux/memblock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/string.h>
#include <linux/threads.h>
#define MTK_MAX_CPU 8
#define MTK_SMP_REG_SIZE 0x1000
struct mtk_smp_boot_info {
unsigned long smp_base;
unsigned int jump_reg;
unsigned int core_keys[MTK_MAX_CPU - 1];
unsigned int core_regs[MTK_MAX_CPU - 1];
};
static const struct mtk_smp_boot_info mtk_mt8135_tz_boot = {
0x80002000, 0x3fc,
{ 0x534c4131, 0x4c415332, 0x41534c33 },
{ 0x3f8, 0x3f8, 0x3f8 },
};
static const struct mtk_smp_boot_info mtk_mt6589_boot = {
0x10002000, 0x34,
{ 0x534c4131, 0x4c415332, 0x41534c33 },
{ 0x38, 0x3c, 0x40 },
};
static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt8135", .data = &mtk_mt8135_tz_boot },
{ .compatible = "mediatek,mt8127", .data = &mtk_mt8135_tz_boot },
};
static const struct of_device_id mtk_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt6589", .data = &mtk_mt6589_boot },
};
static void __iomem *mtk_smp_base;
static const struct mtk_smp_boot_info *mtk_smp_info;
static int mtk_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
if (!mtk_smp_base)
return -EINVAL;
if (!mtk_smp_info->core_keys[cpu-1])
return -EINVAL;
writel_relaxed(mtk_smp_info->core_keys[cpu-1],
mtk_smp_base + mtk_smp_info->core_regs[cpu-1]);
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
return 0;
}
static void __init __mtk_smp_prepare_cpus(unsigned int max_cpus, int trustzone)
{
int i, num;
const struct of_device_id *infos;
if (trustzone) {
num = ARRAY_SIZE(mtk_tz_smp_boot_infos);
infos = mtk_tz_smp_boot_infos;
} else {
num = ARRAY_SIZE(mtk_smp_boot_infos);
infos = mtk_smp_boot_infos;
}
/* Find smp boot info for this SoC */
for (i = 0; i < num; i++) {
if (of_machine_is_compatible(infos[i].compatible)) {
mtk_smp_info = infos[i].data;
break;
}
}
if (!mtk_smp_info) {
pr_err("%s: Device is not supported\n", __func__);
return;
}
if (trustzone) {
/* smp_base(trustzone-bootinfo) is reserved by device tree */
mtk_smp_base = phys_to_virt(mtk_smp_info->smp_base);
} else {
mtk_smp_base = ioremap(mtk_smp_info->smp_base, MTK_SMP_REG_SIZE);
if (!mtk_smp_base) {
pr_err("%s: Can't remap %lx\n", __func__,
mtk_smp_info->smp_base);
return;
}
}
/*
* write the address of slave startup address into the system-wide
* jump register
*/
writel_relaxed(virt_to_phys(secondary_startup_arm),
mtk_smp_base + mtk_smp_info->jump_reg);
}
static void __init mtk_tz_smp_prepare_cpus(unsigned int max_cpus)
{
__mtk_smp_prepare_cpus(max_cpus, 1);
}
static void __init mtk_smp_prepare_cpus(unsigned int max_cpus)
{
__mtk_smp_prepare_cpus(max_cpus, 0);
}
static struct smp_operations mt81xx_tz_smp_ops __initdata = {
.smp_prepare_cpus = mtk_tz_smp_prepare_cpus,
.smp_boot_secondary = mtk_boot_secondary,
};
CPU_METHOD_OF_DECLARE(mt81xx_tz_smp, "mediatek,mt81xx-tz-smp", &mt81xx_tz_smp_ops);
static struct smp_operations mt6589_smp_ops __initdata = {
.smp_prepare_cpus = mtk_smp_prepare_cpus,
.smp_boot_secondary = mtk_boot_secondary,
};
CPU_METHOD_OF_DECLARE(mt6589_smp, "mediatek,mt6589-smp", &mt6589_smp_ops);

View file

@ -19,4 +19,9 @@ config MACH_MESON8
default ARCH_MESON
select MESON6_TIMER
config MACH_MESON8B
bool "Amlogic Meson8b SoCs support"
default ARCH_MESON
select MESON6_TIMER
endif

View file

@ -19,6 +19,7 @@
static const char * const meson_common_board_compat[] = {
"amlogic,meson6",
"amlogic,meson8",
"amlogic,meson8b",
NULL,
};

View file

@ -40,6 +40,7 @@
unsigned long coherency_phys_base;
void __iomem *coherency_base;
static void __iomem *coherency_cpu_base;
static void __iomem *cpu_config_base;
/* Coherency fabric registers */
#define IO_SYNC_BARRIER_CTL_OFFSET 0x0
@ -65,6 +66,31 @@ static const struct of_device_id of_coherency_table[] = {
int ll_enable_coherency(void);
void ll_add_cpu_to_smp_group(void);
#define CPU_CONFIG_SHARED_L2 BIT(16)
/*
* Disable the "Shared L2 Present" bit in CPU Configuration register
* on Armada XP.
*
* The "Shared L2 Present" bit affects the "level of coherence" value
* in the clidr CP15 register. Cache operation functions such as
* "flush all" and "invalidate all" operate on all the cache levels
* that included in the defined level of coherence. When HW I/O
* coherency is used, this bit causes unnecessary flushes of the L2
* cache.
*/
static void armada_xp_clear_shared_l2(void)
{
u32 reg;
if (!cpu_config_base)
return;
reg = readl(cpu_config_base);
reg &= ~CPU_CONFIG_SHARED_L2;
writel(reg, cpu_config_base);
}
static int mvebu_hwcc_notifier(struct notifier_block *nb,
unsigned long event, void *__dev)
{
@ -85,9 +111,24 @@ static struct notifier_block mvebu_hwcc_pci_nb = {
.notifier_call = mvebu_hwcc_notifier,
};
static int armada_xp_clear_shared_l2_notifier_func(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
armada_xp_clear_shared_l2();
return NOTIFY_OK;
}
static struct notifier_block armada_xp_clear_shared_l2_notifier = {
.notifier_call = armada_xp_clear_shared_l2_notifier_func,
.priority = 100,
};
static void __init armada_370_coherency_init(struct device_node *np)
{
struct resource res;
struct device_node *cpu_config_np;
of_address_to_resource(np, 0, &res);
coherency_phys_base = res.start;
@ -100,6 +141,23 @@ static void __init armada_370_coherency_init(struct device_node *np)
sync_cache_w(&coherency_phys_base);
coherency_base = of_iomap(np, 0);
coherency_cpu_base = of_iomap(np, 1);
cpu_config_np = of_find_compatible_node(NULL, NULL,
"marvell,armada-xp-cpu-config");
if (!cpu_config_np)
goto exit;
cpu_config_base = of_iomap(cpu_config_np, 0);
if (!cpu_config_base) {
of_node_put(cpu_config_np);
goto exit;
}
of_node_put(cpu_config_np);
register_cpu_notifier(&armada_xp_clear_shared_l2_notifier);
exit:
set_cpu_coherent();
}
@ -204,6 +262,8 @@ int set_cpu_coherent(void)
pr_warn("Coherency fabric is not initialized\n");
return 1;
}
armada_xp_clear_shared_l2();
ll_add_cpu_to_smp_group();
return ll_enable_coherency();
}

View file

@ -379,6 +379,16 @@ static struct notifier_block mvebu_v7_cpu_pm_notifier = {
static struct platform_device mvebu_v7_cpuidle_device;
static int broken_idle(struct device_node *np)
{
if (of_property_read_bool(np, "broken-idle")) {
pr_warn("CPU idle is currently broken: disabling\n");
return 1;
}
return 0;
}
static __init int armada_370_cpuidle_init(void)
{
struct device_node *np;
@ -387,7 +397,9 @@ static __init int armada_370_cpuidle_init(void)
np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
if (!np)
return -ENODEV;
of_node_put(np);
if (broken_idle(np))
goto end;
/*
* On Armada 370, there is "a slow exit process from the deep
@ -406,6 +418,8 @@ static __init int armada_370_cpuidle_init(void)
mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend;
mvebu_v7_cpuidle_device.name = "cpuidle-armada-370";
end:
of_node_put(np);
return 0;
}
@ -422,6 +436,10 @@ static __init int armada_38x_cpuidle_init(void)
"marvell,armada-380-coherency-fabric");
if (!np)
return -ENODEV;
if (broken_idle(np))
goto end;
of_node_put(np);
np = of_find_compatible_node(NULL, NULL,
@ -430,7 +448,6 @@ static __init int armada_38x_cpuidle_init(void)
return -ENODEV;
mpsoc_base = of_iomap(np, 0);
BUG_ON(!mpsoc_base);
of_node_put(np);
/* Set up reset mask when powering down the cpus */
reg = readl(mpsoc_base + MPCORE_RESET_CTL);
@ -450,6 +467,8 @@ static __init int armada_38x_cpuidle_init(void)
mvebu_v7_cpuidle_device.dev.platform_data = armada_38x_cpu_suspend;
mvebu_v7_cpuidle_device.name = "cpuidle-armada-38x";
end:
of_node_put(np);
return 0;
}
@ -460,12 +479,16 @@ static __init int armada_xp_cpuidle_init(void)
np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
if (!np)
return -ENODEV;
of_node_put(np);
if (broken_idle(np))
goto end;
mvebu_cpu_resume = armada_370_xp_cpu_resume;
mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend;
mvebu_v7_cpuidle_device.name = "cpuidle-armada-xp";
end:
of_node_put(np);
return 0;
}

View file

@ -45,6 +45,7 @@ config MACH_KUROBOX_PRO
config MACH_DNS323
bool "D-Link DNS-323"
select GENERIC_NET_UTILS
select I2C_BOARDINFO
help
Say 'Y' here if you want your kernel to support the
@ -52,6 +53,7 @@ config MACH_DNS323
config MACH_TS209
bool "QNAP TS-109/TS-209"
select GENERIC_NET_UTILS
help
Say 'Y' here if you want your kernel to support the
QNAP TS-109/TS-209 platform.
@ -93,6 +95,7 @@ config MACH_LINKSTATION_LS_HGL
config MACH_TS409
bool "QNAP TS-409"
select GENERIC_NET_UTILS
help
Say 'Y' here if you want your kernel to support the
QNAP TS-409 platform.

View file

@ -173,42 +173,10 @@ static struct mv643xx_eth_platform_data dns323_eth_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(8),
};
/* dns323_parse_hex_*() taken from tsx09-common.c; should a common copy of these
* functions be kept somewhere?
*/
static int __init dns323_parse_hex_nibble(char n)
{
if (n >= '0' && n <= '9')
return n - '0';
if (n >= 'A' && n <= 'F')
return n - 'A' + 10;
if (n >= 'a' && n <= 'f')
return n - 'a' + 10;
return -1;
}
static int __init dns323_parse_hex_byte(const char *b)
{
int hi;
int lo;
hi = dns323_parse_hex_nibble(b[0]);
lo = dns323_parse_hex_nibble(b[1]);
if (hi < 0 || lo < 0)
return -1;
return (hi << 4) | lo;
}
static int __init dns323_read_mac_addr(void)
{
u_int8_t addr[6];
int i;
char *mac_page;
void __iomem *mac_page;
/* MAC address is stored as a regular ol' string in /dev/mtdblock4
* (0x007d0000-0x00800000) starting at offset 196480 (0x2ff80).
@ -217,23 +185,8 @@ static int __init dns323_read_mac_addr(void)
if (!mac_page)
return -ENOMEM;
/* Sanity check the string we're looking at */
for (i = 0; i < 5; i++) {
if (*(mac_page + (i * 3) + 2) != ':') {
goto error_fail;
}
}
for (i = 0; i < 6; i++) {
int byte;
byte = dns323_parse_hex_byte(mac_page + (i * 3));
if (byte < 0) {
goto error_fail;
}
addr[i] = byte;
}
if (!mac_pton((__force const char *) mac_page, addr))
goto error_fail;
iounmap(mac_page);
printk("DNS-323: Found ethernet MAC address: %pM\n", addr);

View file

@ -53,53 +53,12 @@ struct mv643xx_eth_platform_data qnap_tsx09_eth_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(8),
};
static int __init qnap_tsx09_parse_hex_nibble(char n)
{
if (n >= '0' && n <= '9')
return n - '0';
if (n >= 'A' && n <= 'F')
return n - 'A' + 10;
if (n >= 'a' && n <= 'f')
return n - 'a' + 10;
return -1;
}
static int __init qnap_tsx09_parse_hex_byte(const char *b)
{
int hi;
int lo;
hi = qnap_tsx09_parse_hex_nibble(b[0]);
lo = qnap_tsx09_parse_hex_nibble(b[1]);
if (hi < 0 || lo < 0)
return -1;
return (hi << 4) | lo;
}
static int __init qnap_tsx09_check_mac_addr(const char *addr_str)
{
u_int8_t addr[6];
int i;
for (i = 0; i < 6; i++) {
int byte;
/*
* Enforce "xx:xx:xx:xx:xx:xx\n" format.
*/
if (addr_str[(i * 3) + 2] != ((i < 5) ? ':' : '\n'))
return -1;
byte = qnap_tsx09_parse_hex_byte(addr_str + (i * 3));
if (byte < 0)
return -1;
addr[i] = byte;
}
if (!mac_pton(addr_str, addr))
return -1;
printk(KERN_INFO "tsx09: found ethernet mac address %pM\n", addr);
@ -118,12 +77,12 @@ void __init qnap_tsx09_find_mac_addr(u32 mem_base, u32 size)
unsigned long addr;
for (addr = mem_base; addr < (mem_base + size); addr += 1024) {
char *nor_page;
void __iomem *nor_page;
int ret = 0;
nor_page = ioremap(addr, 1024);
if (nor_page != NULL) {
ret = qnap_tsx09_check_mac_addr(nor_page);
ret = qnap_tsx09_check_mac_addr((__force const char *)nor_page);
iounmap(nor_page);
}

View file

@ -25,6 +25,7 @@
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/i2c.h>
#include <linux/leds.h>
@ -469,6 +470,11 @@ static struct s3c24xx_mci_pdata h1940_mmc_cfg __initdata = {
.ocr_avail = MMC_VDD_32_33,
};
static struct pwm_lookup h1940_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 36296,
PWM_POLARITY_NORMAL),
};
static int h1940_backlight_init(struct device *dev)
{
gpio_request(S3C2410_GPB(0), "Backlight");
@ -503,11 +509,8 @@ static void h1940_backlight_exit(struct device *dev)
static struct platform_pwm_backlight_data backlight_data = {
.pwm_id = 0,
.max_brightness = 100,
.dft_brightness = 50,
/* tcnt = 0x31 */
.pwm_period_ns = 36296,
.enable_gpio = -1,
.init = h1940_backlight_init,
.notify = h1940_backlight_notify,
@ -725,6 +728,7 @@ static void __init h1940_init(void)
gpio_request(H1940_LATCH_SD_POWER, "SD power");
gpio_direction_output(H1940_LATCH_SD_POWER, 0);
pwm_add_table(h1940_pwm_lookup, ARRAY_SIZE(h1940_pwm_lookup));
platform_add_devices(h1940_devices, ARRAY_SIZE(h1940_devices));
gpio_request(S3C2410_GPA(1), "Red LED blink");

View file

@ -375,6 +375,11 @@ static struct s3c2410fb_mach_info rx1950_lcd_cfg = {
};
static struct pwm_lookup rx1950_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight.0", NULL, 48000,
PWM_POLARITY_NORMAL),
};
static struct pwm_device *lcd_pwm;
static void rx1950_lcd_power(int enable)
@ -520,10 +525,8 @@ static int rx1950_backlight_notify(struct device *dev, int brightness)
}
static struct platform_pwm_backlight_data rx1950_backlight_data = {
.pwm_id = 0,
.max_brightness = 24,
.dft_brightness = 4,
.pwm_period_ns = 48000,
.enable_gpio = -1,
.init = rx1950_backlight_init,
.notify = rx1950_backlight_notify,
@ -792,6 +795,7 @@ static void __init rx1950_init_machine(void)
gpio_direction_output(S3C2410_GPA(4), 0);
gpio_direction_output(S3C2410_GPJ(6), 0);
pwm_add_table(rx1950_pwm_lookup, ARRAY_SIZE(rx1950_pwm_lookup));
platform_add_devices(rx1950_devices, ARRAY_SIZE(rx1950_devices));
i2c_register_board_info(0, rx1950_i2c_devices,

View file

@ -69,7 +69,6 @@ static struct samsung_bl_drvdata samsung_dfl_bl_data __initdata = {
.plat_data = {
.max_brightness = 255,
.dft_brightness = 255,
.pwm_period_ns = 78770,
.enable_gpio = -1,
.init = samsung_bl_init,
.exit = samsung_bl_exit,
@ -111,7 +110,6 @@ void __init samsung_bl_set(struct samsung_bl_gpio_info *gpio_info,
samsung_bl_data = &samsung_bl_drvdata->plat_data;
/* Copy board specific data provided by user */
samsung_bl_data->pwm_id = bl_data->pwm_id;
samsung_bl_device->dev.parent = &samsung_device_pwm.dev;
if (bl_data->max_brightness)
@ -120,8 +118,6 @@ void __init samsung_bl_set(struct samsung_bl_gpio_info *gpio_info,
samsung_bl_data->dft_brightness = bl_data->dft_brightness;
if (bl_data->lth_brightness)
samsung_bl_data->lth_brightness = bl_data->lth_brightness;
if (bl_data->pwm_period_ns)
samsung_bl_data->pwm_period_ns = bl_data->pwm_period_ns;
if (bl_data->enable_gpio >= 0)
samsung_bl_data->enable_gpio = bl_data->enable_gpio;
if (bl_data->init)

View file

@ -25,6 +25,7 @@
#include <linux/mmc/host.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/fixed.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/dm9000.h>
#include <linux/gpio_keys.h>
@ -108,11 +109,14 @@ static struct s3c2410_uartcfg crag6410_uartcfgs[] __initdata = {
},
};
static struct pwm_lookup crag6410_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 100000,
PWM_POLARITY_NORMAL),
};
static struct platform_pwm_backlight_data crag6410_backlight_data = {
.pwm_id = 0,
.max_brightness = 1000,
.dft_brightness = 600,
.pwm_period_ns = 100000, /* about 1kHz */
.enable_gpio = -1,
};
@ -843,6 +847,7 @@ static void __init crag6410_machine_init(void)
samsung_keypad_set_platdata(&crag6410_keypad_data);
s3c64xx_spi0_set_platdata(NULL, 0, 2);
pwm_add_table(crag6410_pwm_lookup, ARRAY_SIZE(crag6410_pwm_lookup));
platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices));
gpio_led_register_device(-1, &gpio_leds_pdata);

View file

@ -19,6 +19,7 @@
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/leds.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
@ -73,6 +74,11 @@ static struct s3c2410_uartcfg hmt_uartcfgs[] __initdata = {
},
};
static struct pwm_lookup hmt_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL,
1000000000 / (100 * 256 * 20), PWM_POLARITY_NORMAL),
};
static int hmt_bl_init(struct device *dev)
{
int ret;
@ -110,10 +116,8 @@ static void hmt_bl_exit(struct device *dev)
}
static struct platform_pwm_backlight_data hmt_backlight_data = {
.pwm_id = 1,
.max_brightness = 100 * 256,
.dft_brightness = 40 * 256,
.pwm_period_ns = 1000000000 / (100 * 256 * 20),
.enable_gpio = -1,
.init = hmt_bl_init,
.notify = hmt_bl_notify,
@ -268,6 +272,7 @@ static void __init hmt_machine_init(void)
gpio_request(S3C64XX_GPF(13), "usb power");
gpio_direction_output(S3C64XX_GPF(13), 1);
pwm_add_table(hmt_pwm_lookup, ARRAY_SIZE(hmt_pwm_lookup));
platform_add_devices(hmt_devices, ARRAY_SIZE(hmt_devices));
}

View file

@ -14,6 +14,7 @@
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/serial_core.h>
#include <linux/serial_s3c.h>
@ -139,6 +140,11 @@ static struct platform_device smartq_usb_otg_vbus_dev = {
.dev.platform_data = &smartq_usb_otg_vbus_pdata,
};
static struct pwm_lookup smartq_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL,
1000000000 / (1000 * 20), PWM_POLARITY_NORMAL),
};
static int smartq_bl_init(struct device *dev)
{
s3c_gpio_cfgpin(S3C64XX_GPF(15), S3C_GPIO_SFN(2));
@ -147,10 +153,8 @@ static int smartq_bl_init(struct device *dev)
}
static struct platform_pwm_backlight_data smartq_backlight_data = {
.pwm_id = 1,
.max_brightness = 1000,
.dft_brightness = 600,
.pwm_period_ns = 1000000000 / (1000 * 20),
.enable_gpio = -1,
.init = smartq_bl_init,
};
@ -396,5 +400,6 @@ void __init smartq_machine_init(void)
WARN_ON(smartq_usb_host_init());
WARN_ON(smartq_wifi_init());
pwm_add_table(smartq_pwm_lookup, ARRAY_SIZE(smartq_pwm_lookup));
platform_add_devices(smartq_devices, ARRAY_SIZE(smartq_devices));
}

View file

@ -30,6 +30,7 @@
#include <linux/smsc911x.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/platform_data/s3c-hsotg.h>
@ -623,8 +624,12 @@ static struct samsung_bl_gpio_info smdk6410_bl_gpio_info = {
.func = S3C_GPIO_SFN(2),
};
static struct pwm_lookup smdk6410_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL, 78770,
PWM_POLARITY_NORMAL),
};
static struct platform_pwm_backlight_data smdk6410_bl_data = {
.pwm_id = 1,
.enable_gpio = -1,
};
@ -695,6 +700,7 @@ static void __init smdk6410_machine_init(void)
platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
pwm_add_table(smdk6410_pwm_lookup, ARRAY_SIZE(smdk6410_pwm_lookup));
samsung_bl_set(&smdk6410_bl_gpio_info, &smdk6410_bl_data);
}

View file

@ -12,6 +12,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/clk/shmobile.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/of.h>
@ -124,36 +125,6 @@ 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;
@ -164,8 +135,8 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
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;
genpd->attach_dev = cpg_mstp_attach_dev;
genpd->detach_dev = cpg_mstp_detach_dev;
__rmobile_pd_power_up(rmobile_pd, false);
}

View file

@ -26,10 +26,11 @@ static const char * const sunxi_board_dt_compat[] = {
"allwinner,sun4i-a10",
"allwinner,sun5i-a10s",
"allwinner,sun5i-a13",
"allwinner,sun5i-r8",
NULL,
};
DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)")
DT_MACHINE_START(SUNXI_DT, "Allwinner sun4i/sun5i Families")
.dt_compat = sunxi_board_dt_compat,
.init_late = sunxi_dt_cpufreq_init,
MACHINE_END

View file

@ -39,8 +39,8 @@ static struct platform_device wifi_rfkill_device = {
static struct gpiod_lookup_table wifi_gpio_lookup = {
.dev_id = "rfkill_gpio",
.table = {
GPIO_LOOKUP_IDX("tegra-gpio", 25, NULL, 0, 0),
GPIO_LOOKUP_IDX("tegra-gpio", 85, NULL, 1, 0),
GPIO_LOOKUP("tegra-gpio", 25, "reset", 0),
GPIO_LOOKUP("tegra-gpio", 85, "shutdown", 0),
{ },
},
};

View file

@ -1,2 +1,2 @@
obj-y := uniphier.o
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.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, 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 <asm/assembler.h>
#include <asm/cp15.h>
ENTRY(uniphier_smp_trampoline)
ARM_BE8(setend be) @ ensure we are in BE8 mode
mrc p15, 0, r0, c0, c0, 5 @ MPIDR (Multiprocessor Affinity Reg)
and r2, r0, #0x3 @ CPU ID
ldr r1, uniphier_smp_trampoline_jump
ldr r3, uniphier_smp_trampoline_poll_addr
mrc p15, 0, r0, c1, c0, 0 @ SCTLR (System Control Register)
orr r0, r0, #CR_I @ Enable ICache
bic r0, r0, #(CR_C | CR_M) @ Disable MMU and Dcache
mcr p15, 0, r0, c1, c0, 0
b 1f @ cache the following 5 instructions
0: wfe
1: ldr r0, [r3]
cmp r0, r2
bxeq r1 @ branch to secondary_startup
b 0b
.globl uniphier_smp_trampoline_jump
uniphier_smp_trampoline_jump:
.word 0 @ set virt_to_phys(secondary_startup)
.globl uniphier_smp_trampoline_poll_addr
uniphier_smp_trampoline_poll_addr:
.word 0 @ set CPU ID to be kicked to this reg
.globl uniphier_smp_trampoline_end
uniphier_smp_trampoline_end:
ENDPROC(uniphier_smp_trampoline)

View file

@ -12,73 +12,198 @@
* GNU General Public License for more details.
*/
#include <linux/sizes.h>
#include <linux/compiler.h>
#define pr_fmt(fmt) "uniphier: " fmt
#include <linux/init.h>
#include <linux/io.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/sizes.h>
#include <asm/cacheflush.h>
#include <asm/hardware/cache-uniphier.h>
#include <asm/pgtable.h>
#include <asm/smp.h>
#include <asm/smp_scu.h>
static struct regmap *sbcm_regmap;
/*
* The secondary CPUs check this register from the boot ROM for the jump
* destination. After that, it can be reused as a scratch register.
*/
#define UNIPHIER_SBC_ROM_BOOT_RSV2 0x1208
static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
static void __iomem *uniphier_smp_rom_boot_rsv2;
static unsigned int uniphier_smp_max_cpus;
extern char uniphier_smp_trampoline;
extern char uniphier_smp_trampoline_jump;
extern char uniphier_smp_trampoline_poll_addr;
extern char uniphier_smp_trampoline_end;
/*
* Copy trampoline code to the tail of the 1st section of the page table used
* in the boot ROM. This area is directly accessible by the secondary CPUs
* for all the UniPhier SoCs.
*/
static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE;
static phys_addr_t uniphier_smp_trampoline_dest;
static int __init uniphier_smp_copy_trampoline(phys_addr_t poll_addr)
{
size_t trmp_size;
static void __iomem *trmp_base;
if (!uniphier_cache_l2_is_enabled()) {
pr_warn("outer cache is needed for SMP, but not enabled\n");
return -ENODEV;
}
uniphier_cache_l2_set_locked_ways(1);
outer_flush_all();
trmp_size = &uniphier_smp_trampoline_end - &uniphier_smp_trampoline;
uniphier_smp_trampoline_dest = uniphier_smp_trampoline_dest_end -
trmp_size;
uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest,
uniphier_smp_trampoline_dest_end);
trmp_base = ioremap_cache(uniphier_smp_trampoline_dest, trmp_size);
if (!trmp_base) {
pr_err("failed to map trampoline destination area\n");
return -ENOMEM;
}
memcpy(trmp_base, &uniphier_smp_trampoline, trmp_size);
writel(virt_to_phys(secondary_startup),
trmp_base + (&uniphier_smp_trampoline_jump -
&uniphier_smp_trampoline));
writel(poll_addr, trmp_base + (&uniphier_smp_trampoline_poll_addr -
&uniphier_smp_trampoline));
flush_cache_all(); /* flush out trampoline code to outer cache */
iounmap(trmp_base);
return 0;
}
static int __init uniphier_smp_prepare_trampoline(unsigned int max_cpus)
{
struct device_node *np;
struct resource res;
phys_addr_t rom_rsv2_phys;
int ret;
np = of_find_compatible_node(NULL, NULL,
"socionext,uniphier-system-bus-controller");
ret = of_address_to_resource(np, 1, &res);
if (ret) {
pr_err("failed to get resource of system-bus-controller\n");
return ret;
}
rom_rsv2_phys = res.start + UNIPHIER_SBC_ROM_BOOT_RSV2;
ret = uniphier_smp_copy_trampoline(rom_rsv2_phys);
if (ret)
return ret;
uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, sizeof(SZ_4));
if (!uniphier_smp_rom_boot_rsv2) {
pr_err("failed to map ROM_BOOT_RSV2 register\n");
return -ENOMEM;
}
writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2);
asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
uniphier_smp_max_cpus = max_cpus; /* save for later use */
return 0;
}
static void __init uniphier_smp_unprepare_trampoline(void)
{
iounmap(uniphier_smp_rom_boot_rsv2);
if (uniphier_smp_trampoline_dest)
outer_inv_range(uniphier_smp_trampoline_dest,
uniphier_smp_trampoline_dest_end);
uniphier_cache_l2_set_locked_ways(0);
}
static int __init uniphier_smp_enable_scu(void)
{
static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
unsigned long scu_base_phys = 0;
void __iomem *scu_base;
sbcm_regmap = syscon_regmap_lookup_by_compatible(
"socionext,uniphier-system-bus-controller-misc");
if (IS_ERR(sbcm_regmap)) {
pr_err("failed to regmap system-bus-controller-misc\n");
goto err;
}
if (scu_a9_has_base())
scu_base_phys = scu_a9_get_base();
if (!scu_base_phys) {
pr_err("failed to get scu base\n");
goto err;
return -ENODEV;
}
scu_base = ioremap(scu_base_phys, SZ_128);
if (!scu_base) {
pr_err("failed to remap scu base (0x%08lx)\n", scu_base_phys);
goto err;
pr_err("failed to map scu base\n");
return -ENOMEM;
}
scu_enable(scu_base);
iounmap(scu_base);
return 0;
}
static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
{
static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
int ret;
ret = uniphier_smp_prepare_trampoline(max_cpus);
if (ret)
goto err;
ret = uniphier_smp_enable_scu();
if (ret)
goto err;
return;
err:
pr_warn("disabling SMP\n");
init_cpu_present(&only_cpu_0);
sbcm_regmap = NULL;
uniphier_smp_unprepare_trampoline();
}
static int uniphier_boot_secondary(unsigned int cpu,
struct task_struct *idle)
static int __init uniphier_smp_boot_secondary(unsigned int cpu,
struct task_struct *idle)
{
int ret;
if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2))
return -EFAULT;
if (!sbcm_regmap)
return -ENODEV;
writel(cpu, uniphier_smp_rom_boot_rsv2);
readl(uniphier_smp_rom_boot_rsv2); /* relax */
ret = regmap_write(sbcm_regmap, 0x1208,
virt_to_phys(secondary_startup));
if (!ret)
asm("sev"); /* wake up secondary CPU */
asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
return ret;
if (cpu == uniphier_smp_max_cpus - 1) {
/* clean up resources if this is the last CPU */
uniphier_smp_unprepare_trampoline();
}
return 0;
}
struct smp_operations uniphier_smp_ops __initdata = {
static struct smp_operations uniphier_smp_ops __initdata = {
.smp_prepare_cpus = uniphier_smp_prepare_cpus,
.smp_boot_secondary = uniphier_boot_secondary,
.smp_boot_secondary = uniphier_smp_boot_secondary,
};
CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp",
&uniphier_smp_ops);

View file

@ -974,6 +974,16 @@ config CACHE_TAUROS2
This option enables the Tauros2 L2 cache controller (as
found on PJ1/PJ4).
config CACHE_UNIPHIER
bool "Enable the UniPhier outer cache controller"
depends on ARCH_UNIPHIER
default y
select OUTER_CACHE
select OUTER_CACHE_SYNC
help
This option enables the UniPhier outer cache (system cache)
controller.
config CACHE_XSC3L2
bool "Enable the L2 cache on XScale3"
depends on CPU_XSC3

View file

@ -103,3 +103,4 @@ obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o l2c-l2x0-resume.o
obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o
obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o
obj-$(CONFIG_CACHE_UNIPHIER) += cache-uniphier.o

View file

@ -0,0 +1,555 @@
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.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, 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.
*/
#define pr_fmt(fmt) "uniphier: " fmt
#include <linux/init.h>
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <asm/hardware/cache-uniphier.h>
#include <asm/outercache.h>
/* control registers */
#define UNIPHIER_SSCC 0x0 /* Control Register */
#define UNIPHIER_SSCC_BST BIT(20) /* UCWG burst read */
#define UNIPHIER_SSCC_ACT BIT(19) /* Inst-Data separate */
#define UNIPHIER_SSCC_WTG BIT(18) /* WT gathering on */
#define UNIPHIER_SSCC_PRD BIT(17) /* enable pre-fetch */
#define UNIPHIER_SSCC_ON BIT(0) /* enable cache */
#define UNIPHIER_SSCLPDAWCR 0x30 /* Unified/Data Active Way Control */
#define UNIPHIER_SSCLPIAWCR 0x34 /* Instruction Active Way Control */
/* revision registers */
#define UNIPHIER_SSCID 0x0 /* ID Register */
/* operation registers */
#define UNIPHIER_SSCOPE 0x244 /* Cache Operation Primitive Entry */
#define UNIPHIER_SSCOPE_CM_INV 0x0 /* invalidate */
#define UNIPHIER_SSCOPE_CM_CLEAN 0x1 /* clean */
#define UNIPHIER_SSCOPE_CM_FLUSH 0x2 /* flush */
#define UNIPHIER_SSCOPE_CM_SYNC 0x8 /* sync (drain bufs) */
#define UNIPHIER_SSCOPE_CM_FLUSH_PREFETCH 0x9 /* flush p-fetch buf */
#define UNIPHIER_SSCOQM 0x248 /* Cache Operation Queue Mode */
#define UNIPHIER_SSCOQM_TID_MASK (0x3 << 21)
#define UNIPHIER_SSCOQM_TID_LRU_DATA (0x0 << 21)
#define UNIPHIER_SSCOQM_TID_LRU_INST (0x1 << 21)
#define UNIPHIER_SSCOQM_TID_WAY (0x2 << 21)
#define UNIPHIER_SSCOQM_S_MASK (0x3 << 17)
#define UNIPHIER_SSCOQM_S_RANGE (0x0 << 17)
#define UNIPHIER_SSCOQM_S_ALL (0x1 << 17)
#define UNIPHIER_SSCOQM_S_WAY (0x2 << 17)
#define UNIPHIER_SSCOQM_CE BIT(15) /* notify completion */
#define UNIPHIER_SSCOQM_CM_INV 0x0 /* invalidate */
#define UNIPHIER_SSCOQM_CM_CLEAN 0x1 /* clean */
#define UNIPHIER_SSCOQM_CM_FLUSH 0x2 /* flush */
#define UNIPHIER_SSCOQM_CM_PREFETCH 0x3 /* prefetch to cache */
#define UNIPHIER_SSCOQM_CM_PREFETCH_BUF 0x4 /* prefetch to pf-buf */
#define UNIPHIER_SSCOQM_CM_TOUCH 0x5 /* touch */
#define UNIPHIER_SSCOQM_CM_TOUCH_ZERO 0x6 /* touch to zero */
#define UNIPHIER_SSCOQM_CM_TOUCH_DIRTY 0x7 /* touch with dirty */
#define UNIPHIER_SSCOQAD 0x24c /* Cache Operation Queue Address */
#define UNIPHIER_SSCOQSZ 0x250 /* Cache Operation Queue Size */
#define UNIPHIER_SSCOQMASK 0x254 /* Cache Operation Queue Address Mask */
#define UNIPHIER_SSCOQWN 0x258 /* Cache Operation Queue Way Number */
#define UNIPHIER_SSCOPPQSEF 0x25c /* Cache Operation Queue Set Complete*/
#define UNIPHIER_SSCOPPQSEF_FE BIT(1)
#define UNIPHIER_SSCOPPQSEF_OE BIT(0)
#define UNIPHIER_SSCOLPQS 0x260 /* Cache Operation Queue Status */
#define UNIPHIER_SSCOLPQS_EF BIT(2)
#define UNIPHIER_SSCOLPQS_EST BIT(1)
#define UNIPHIER_SSCOLPQS_QST BIT(0)
/* Is the touch/pre-fetch destination specified by ways? */
#define UNIPHIER_SSCOQM_TID_IS_WAY(op) \
((op & UNIPHIER_SSCOQM_TID_MASK) == UNIPHIER_SSCOQM_TID_WAY)
/* Is the operation region specified by address range? */
#define UNIPHIER_SSCOQM_S_IS_RANGE(op) \
((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_RANGE)
/**
* uniphier_cache_data - UniPhier outer cache specific data
*
* @ctrl_base: virtual base address of control registers
* @rev_base: virtual base address of revision registers
* @op_base: virtual base address of operation registers
* @way_present_mask: each bit specifies if the way is present
* @way_locked_mask: each bit specifies if the way is locked
* @nsets: number of associativity sets
* @line_size: line size in bytes
* @range_op_max_size: max size that can be handled by a single range operation
* @list: list node to include this level in the whole cache hierarchy
*/
struct uniphier_cache_data {
void __iomem *ctrl_base;
void __iomem *rev_base;
void __iomem *op_base;
u32 way_present_mask;
u32 way_locked_mask;
u32 nsets;
u32 line_size;
u32 range_op_max_size;
struct list_head list;
};
/*
* List of the whole outer cache hierarchy. This list is only modified during
* the early boot stage, so no mutex is taken for the access to the list.
*/
static LIST_HEAD(uniphier_cache_list);
/**
* __uniphier_cache_sync - perform a sync point for a particular cache level
*
* @data: cache controller specific data
*/
static void __uniphier_cache_sync(struct uniphier_cache_data *data)
{
/* This sequence need not be atomic. Do not disable IRQ. */
writel_relaxed(UNIPHIER_SSCOPE_CM_SYNC,
data->op_base + UNIPHIER_SSCOPE);
/* need a read back to confirm */
readl_relaxed(data->op_base + UNIPHIER_SSCOPE);
}
/**
* __uniphier_cache_maint_common - run a queue operation for a particular level
*
* @data: cache controller specific data
* @start: start address of range operation (don't care for "all" operation)
* @size: data size of range operation (don't care for "all" operation)
* @operation: flags to specify the desired cache operation
*/
static void __uniphier_cache_maint_common(struct uniphier_cache_data *data,
unsigned long start,
unsigned long size,
u32 operation)
{
unsigned long flags;
/*
* No spin lock is necessary here because:
*
* [1] This outer cache controller is able to accept maintenance
* operations from multiple CPUs at a time in an SMP system; if a
* maintenance operation is under way and another operation is issued,
* the new one is stored in the queue. The controller performs one
* operation after another. If the queue is full, the status register,
* UNIPHIER_SSCOPPQSEF, indicates that the queue registration has
* failed. The status registers, UNIPHIER_{SSCOPPQSEF, SSCOLPQS}, have
* different instances for each CPU, i.e. each CPU can track the status
* of the maintenance operations triggered by itself.
*
* [2] The cache command registers, UNIPHIER_{SSCOQM, SSCOQAD, SSCOQSZ,
* SSCOQWN}, are shared between multiple CPUs, but the hardware still
* guarantees the registration sequence is atomic; the write access to
* them are arbitrated by the hardware. The first accessor to the
* register, UNIPHIER_SSCOQM, holds the access right and it is released
* by reading the status register, UNIPHIER_SSCOPPQSEF. While one CPU
* is holding the access right, other CPUs fail to register operations.
* One CPU should not hold the access right for a long time, so local
* IRQs should be disabled while the following sequence.
*/
local_irq_save(flags);
/* clear the complete notification flag */
writel_relaxed(UNIPHIER_SSCOLPQS_EF, data->op_base + UNIPHIER_SSCOLPQS);
do {
/* set cache operation */
writel_relaxed(UNIPHIER_SSCOQM_CE | operation,
data->op_base + UNIPHIER_SSCOQM);
/* set address range if needed */
if (likely(UNIPHIER_SSCOQM_S_IS_RANGE(operation))) {
writel_relaxed(start, data->op_base + UNIPHIER_SSCOQAD);
writel_relaxed(size, data->op_base + UNIPHIER_SSCOQSZ);
}
/* set target ways if needed */
if (unlikely(UNIPHIER_SSCOQM_TID_IS_WAY(operation)))
writel_relaxed(data->way_locked_mask,
data->op_base + UNIPHIER_SSCOQWN);
} while (unlikely(readl_relaxed(data->op_base + UNIPHIER_SSCOPPQSEF) &
(UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE)));
/* wait until the operation is completed */
while (likely(readl_relaxed(data->op_base + UNIPHIER_SSCOLPQS) !=
UNIPHIER_SSCOLPQS_EF))
cpu_relax();
local_irq_restore(flags);
}
static void __uniphier_cache_maint_all(struct uniphier_cache_data *data,
u32 operation)
{
__uniphier_cache_maint_common(data, 0, 0,
UNIPHIER_SSCOQM_S_ALL | operation);
__uniphier_cache_sync(data);
}
static void __uniphier_cache_maint_range(struct uniphier_cache_data *data,
unsigned long start, unsigned long end,
u32 operation)
{
unsigned long size;
/*
* If the start address is not aligned,
* perform a cache operation for the first cache-line
*/
start = start & ~(data->line_size - 1);
size = end - start;
if (unlikely(size >= (unsigned long)(-data->line_size))) {
/* this means cache operation for all range */
__uniphier_cache_maint_all(data, operation);
return;
}
/*
* If the end address is not aligned,
* perform a cache operation for the last cache-line
*/
size = ALIGN(size, data->line_size);
while (size) {
unsigned long chunk_size = min_t(unsigned long, size,
data->range_op_max_size);
__uniphier_cache_maint_common(data, start, chunk_size,
UNIPHIER_SSCOQM_S_RANGE | operation);
start += chunk_size;
size -= chunk_size;
}
__uniphier_cache_sync(data);
}
static void __uniphier_cache_enable(struct uniphier_cache_data *data, bool on)
{
u32 val = 0;
if (on)
val = UNIPHIER_SSCC_WTG | UNIPHIER_SSCC_PRD | UNIPHIER_SSCC_ON;
writel_relaxed(val, data->ctrl_base + UNIPHIER_SSCC);
}
static void __init __uniphier_cache_set_locked_ways(
struct uniphier_cache_data *data,
u32 way_mask)
{
data->way_locked_mask = way_mask & data->way_present_mask;
writel_relaxed(~data->way_locked_mask & data->way_present_mask,
data->ctrl_base + UNIPHIER_SSCLPDAWCR);
}
static void uniphier_cache_maint_range(unsigned long start, unsigned long end,
u32 operation)
{
struct uniphier_cache_data *data;
list_for_each_entry(data, &uniphier_cache_list, list)
__uniphier_cache_maint_range(data, start, end, operation);
}
static void uniphier_cache_maint_all(u32 operation)
{
struct uniphier_cache_data *data;
list_for_each_entry(data, &uniphier_cache_list, list)
__uniphier_cache_maint_all(data, operation);
}
static void uniphier_cache_inv_range(unsigned long start, unsigned long end)
{
uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_INV);
}
static void uniphier_cache_clean_range(unsigned long start, unsigned long end)
{
uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_CLEAN);
}
static void uniphier_cache_flush_range(unsigned long start, unsigned long end)
{
uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_FLUSH);
}
static void __init uniphier_cache_inv_all(void)
{
uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_INV);
}
static void uniphier_cache_flush_all(void)
{
uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_FLUSH);
}
static void uniphier_cache_disable(void)
{
struct uniphier_cache_data *data;
list_for_each_entry_reverse(data, &uniphier_cache_list, list)
__uniphier_cache_enable(data, false);
uniphier_cache_flush_all();
}
static void __init uniphier_cache_enable(void)
{
struct uniphier_cache_data *data;
uniphier_cache_inv_all();
list_for_each_entry(data, &uniphier_cache_list, list) {
__uniphier_cache_enable(data, true);
__uniphier_cache_set_locked_ways(data, 0);
}
}
static void uniphier_cache_sync(void)
{
struct uniphier_cache_data *data;
list_for_each_entry(data, &uniphier_cache_list, list)
__uniphier_cache_sync(data);
}
int __init uniphier_cache_l2_is_enabled(void)
{
struct uniphier_cache_data *data;
data = list_first_entry_or_null(&uniphier_cache_list,
struct uniphier_cache_data, list);
if (!data)
return 0;
return !!(readl_relaxed(data->ctrl_base + UNIPHIER_SSCC) &
UNIPHIER_SSCC_ON);
}
void __init uniphier_cache_l2_touch_range(unsigned long start,
unsigned long end)
{
struct uniphier_cache_data *data;
data = list_first_entry_or_null(&uniphier_cache_list,
struct uniphier_cache_data, list);
if (data)
__uniphier_cache_maint_range(data, start, end,
UNIPHIER_SSCOQM_TID_WAY |
UNIPHIER_SSCOQM_CM_TOUCH);
}
void __init uniphier_cache_l2_set_locked_ways(u32 way_mask)
{
struct uniphier_cache_data *data;
data = list_first_entry_or_null(&uniphier_cache_list,
struct uniphier_cache_data, list);
if (data)
__uniphier_cache_set_locked_ways(data, way_mask);
}
static const struct of_device_id uniphier_cache_match[] __initconst = {
{
.compatible = "socionext,uniphier-system-cache",
},
{ /* sentinel */ }
};
static struct device_node * __init uniphier_cache_get_next_level_node(
struct device_node *np)
{
u32 phandle;
if (of_property_read_u32(np, "next-level-cache", &phandle))
return NULL;
return of_find_node_by_phandle(phandle);
}
static int __init __uniphier_cache_init(struct device_node *np,
unsigned int *cache_level)
{
struct uniphier_cache_data *data;
u32 level, cache_size;
struct device_node *next_np;
int ret = 0;
if (!of_match_node(uniphier_cache_match, np)) {
pr_err("L%d: not compatible with uniphier cache\n",
*cache_level);
return -EINVAL;
}
if (of_property_read_u32(np, "cache-level", &level)) {
pr_err("L%d: cache-level is not specified\n", *cache_level);
return -EINVAL;
}
if (level != *cache_level) {
pr_err("L%d: cache-level is unexpected value %d\n",
*cache_level, level);
return -EINVAL;
}
if (!of_property_read_bool(np, "cache-unified")) {
pr_err("L%d: cache-unified is not specified\n", *cache_level);
return -EINVAL;
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
if (of_property_read_u32(np, "cache-line-size", &data->line_size) ||
!is_power_of_2(data->line_size)) {
pr_err("L%d: cache-line-size is unspecified or invalid\n",
*cache_level);
ret = -EINVAL;
goto err;
}
if (of_property_read_u32(np, "cache-sets", &data->nsets) ||
!is_power_of_2(data->nsets)) {
pr_err("L%d: cache-sets is unspecified or invalid\n",
*cache_level);
ret = -EINVAL;
goto err;
}
if (of_property_read_u32(np, "cache-size", &cache_size) ||
cache_size == 0 || cache_size % (data->nsets * data->line_size)) {
pr_err("L%d: cache-size is unspecified or invalid\n",
*cache_level);
ret = -EINVAL;
goto err;
}
data->way_present_mask =
((u32)1 << cache_size / data->nsets / data->line_size) - 1;
data->ctrl_base = of_iomap(np, 0);
if (!data->ctrl_base) {
pr_err("L%d: failed to map control register\n", *cache_level);
ret = -ENOMEM;
goto err;
}
data->rev_base = of_iomap(np, 1);
if (!data->rev_base) {
pr_err("L%d: failed to map revision register\n", *cache_level);
ret = -ENOMEM;
goto err;
}
data->op_base = of_iomap(np, 2);
if (!data->op_base) {
pr_err("L%d: failed to map operation register\n", *cache_level);
ret = -ENOMEM;
goto err;
}
if (*cache_level == 2) {
u32 revision = readl(data->rev_base + UNIPHIER_SSCID);
/*
* The size of range operation is limited to (1 << 22) or less
* for PH-sLD8 or older SoCs.
*/
if (revision <= 0x16)
data->range_op_max_size = (u32)1 << 22;
}
data->range_op_max_size -= data->line_size;
INIT_LIST_HEAD(&data->list);
list_add_tail(&data->list, &uniphier_cache_list); /* no mutex */
/*
* OK, this level has been successfully initialized. Look for the next
* level cache. Do not roll back even if the initialization of the
* next level cache fails because we want to continue with available
* cache levels.
*/
next_np = uniphier_cache_get_next_level_node(np);
if (next_np) {
(*cache_level)++;
ret = __uniphier_cache_init(next_np, cache_level);
}
of_node_put(next_np);
return ret;
err:
iounmap(data->op_base);
iounmap(data->rev_base);
iounmap(data->ctrl_base);
kfree(data);
return ret;
}
int __init uniphier_cache_init(void)
{
struct device_node *np = NULL;
unsigned int cache_level;
int ret = 0;
/* look for level 2 cache */
while ((np = of_find_matching_node(np, uniphier_cache_match)))
if (!of_property_read_u32(np, "cache-level", &cache_level) &&
cache_level == 2)
break;
if (!np)
return -ENODEV;
ret = __uniphier_cache_init(np, &cache_level);
of_node_put(np);
if (ret) {
/*
* Error out iif L2 initialization fails. Continue with any
* error on L3 or outer because they are optional.
*/
if (cache_level == 2) {
pr_err("failed to initialize L2 cache\n");
return ret;
}
cache_level--;
ret = 0;
}
outer_cache.inv_range = uniphier_cache_inv_range;
outer_cache.clean_range = uniphier_cache_clean_range;
outer_cache.flush_range = uniphier_cache_flush_range;
outer_cache.flush_all = uniphier_cache_flush_all;
outer_cache.disable = uniphier_cache_disable;
outer_cache.sync = uniphier_cache_sync;
uniphier_cache_enable();
pr_info("enabled outer cache (cache level: %d)\n", cache_level);
return ret;
}