mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 17:12:06 +00:00
Merge branch 'next/soc' into next/multiplatform
* next/soc: (50 commits) ARM: OMAP: AM33xx hwmod: fixup SPI after platform_data move MAINTAINERS: add an entry for the BCM2835 ARM sub-architecture ARM: bcm2835: instantiate console UART ARM: bcm2835: add stub clock driver ARM: bcm2835: add system timer ARM: bcm2835: add interrupt controller driver ARM: add infra-structure for BCM2835 and Raspberry Pi ARM: tegra20: add CPU hotplug support ARM: tegra30: add CPU hotplug support ARM: tegra: clean up the common assembly macros into sleep.h ARM: tegra: replace the CPU CAR access code by tegra_cpu_car_ops ARM: tegra: introduce tegra_cpu_car_ops structures ARM: Tegra: Add smp_twd clock for Tegra20 ARM: AM33XX: clock: Add dcan clock aliases for device-tree ARM: OMAP2+: dpll: Add missing soc_is_am33xx() check for common functions ARM: OMAP: omap_device: idle devices with no driver bound ARM: OMAP: omap_device: don't attempt late suspend if no driver bound ARM: OMAP: omap_device: keep track of driver bound status ARM: OMAP3+: hwmod: Add AM33XX HWMOD data ARM: OMAP2+: hwmod: Hook-up am33xx support in omap_hwmod framework ... Change/remove conflict in arch/arm/mach-ux500/clock.c resolved. Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
b612a85792
8
Documentation/devicetree/bindings/arm/bcm2835.txt
Normal file
8
Documentation/devicetree/bindings/arm/bcm2835.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Broadcom BCM2835 device tree bindings
|
||||
-------------------------------------------
|
||||
|
||||
Boards with the BCM2835 SoC shall have the following properties:
|
||||
|
||||
Required root node property:
|
||||
|
||||
compatible = "brcm,bcm2835";
|
@ -0,0 +1,110 @@
|
||||
BCM2835 Top-Level ("ARMCTRL") Interrupt Controller
|
||||
|
||||
The BCM2835 contains a custom top-level interrupt controller, which supports
|
||||
72 interrupt sources using a 2-level register scheme. The interrupt
|
||||
controller, or the HW block containing it, is referred to occasionally
|
||||
as "armctrl" in the SoC documentation, hence naming of this binding.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "brcm,bcm2835-armctrl-ic.txt"
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- interrupt-controller : Identifies the node as an interrupt controller
|
||||
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||
interrupt source. The value shall be 2.
|
||||
|
||||
The 1st cell is the interrupt bank; 0 for interrupts in the "IRQ basic
|
||||
pending" register, or 1/2 respectively for interrupts in the "IRQ pending
|
||||
1/2" register.
|
||||
|
||||
The 2nd cell contains the interrupt number within the bank. Valid values
|
||||
are 0..7 for bank 0, and 0..31 for bank 1.
|
||||
|
||||
The interrupt sources are as follows:
|
||||
|
||||
Bank 0:
|
||||
0: ARM_TIMER
|
||||
1: ARM_MAILBOX
|
||||
2: ARM_DOORBELL_0
|
||||
3: ARM_DOORBELL_1
|
||||
4: VPU0_HALTED
|
||||
5: VPU1_HALTED
|
||||
6: ILLEGAL_TYPE0
|
||||
7: ILLEGAL_TYPE1
|
||||
|
||||
Bank 1:
|
||||
0: TIMER0
|
||||
1: TIMER1
|
||||
2: TIMER2
|
||||
3: TIMER3
|
||||
4: CODEC0
|
||||
5: CODEC1
|
||||
6: CODEC2
|
||||
7: VC_JPEG
|
||||
8: ISP
|
||||
9: VC_USB
|
||||
10: VC_3D
|
||||
11: TRANSPOSER
|
||||
12: MULTICORESYNC0
|
||||
13: MULTICORESYNC1
|
||||
14: MULTICORESYNC2
|
||||
15: MULTICORESYNC3
|
||||
16: DMA0
|
||||
17: DMA1
|
||||
18: VC_DMA2
|
||||
19: VC_DMA3
|
||||
20: DMA4
|
||||
21: DMA5
|
||||
22: DMA6
|
||||
23: DMA7
|
||||
24: DMA8
|
||||
25: DMA9
|
||||
26: DMA10
|
||||
27: DMA11
|
||||
28: DMA12
|
||||
29: AUX
|
||||
30: ARM
|
||||
31: VPUDMA
|
||||
|
||||
Bank 2:
|
||||
0: HOSTPORT
|
||||
1: VIDEOSCALER
|
||||
2: CCP2TX
|
||||
3: SDC
|
||||
4: DSI0
|
||||
5: AVE
|
||||
6: CAM0
|
||||
7: CAM1
|
||||
8: HDMI0
|
||||
9: HDMI1
|
||||
10: PIXELVALVE1
|
||||
11: I2CSPISLV
|
||||
12: DSI1
|
||||
13: PWA0
|
||||
14: PWA1
|
||||
15: CPR
|
||||
16: SMI
|
||||
17: GPIO0
|
||||
18: GPIO1
|
||||
19: GPIO2
|
||||
20: GPIO3
|
||||
21: VC_I2C
|
||||
22: VC_SPI
|
||||
23: VC_I2SPCM
|
||||
24: VC_SDIO
|
||||
25: VC_UART
|
||||
26: SLIMBUS
|
||||
27: VEC
|
||||
28: CPG
|
||||
29: RNG
|
||||
30: VC_ARASANSDIO
|
||||
31: AVSPMON
|
||||
|
||||
Example:
|
||||
|
||||
intc: interrupt-controller {
|
||||
compatible = "brcm,bcm2835-armctrl-ic";
|
||||
reg = <0x7e00b200 0x200>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
BCM2835 System Timer
|
||||
|
||||
The System Timer peripheral provides four 32-bit timer channels and a
|
||||
single 64-bit free running counter. Each channel has an output compare
|
||||
register, which is compared against the 32 least significant bits of the
|
||||
free running counter values, and generates an interrupt.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "brcm,bcm2835-system-timer.txt"
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- interrupts : A list of 4 interrupt sinks; one per timer channel.
|
||||
- clock-frequency : The frequency of the clock that drives the counter, in Hz.
|
||||
|
||||
Example:
|
||||
|
||||
timer {
|
||||
compatible = "brcm,bcm2835-system-timer";
|
||||
reg = <0x7e003000 0x1000>;
|
||||
interrupts = <1 0>, <1 1>, <1 2>, <1 3>;
|
||||
clock-frequency = <1000000>;
|
||||
};
|
@ -10,6 +10,7 @@ apm Applied Micro Circuits Corporation (APM)
|
||||
arm ARM Ltd.
|
||||
atmel Atmel Corporation
|
||||
bosch Bosch Sensortec GmbH
|
||||
brcm Broadcom Corporation
|
||||
cavium Cavium, Inc.
|
||||
chrp Common Hardware Reference Platform
|
||||
cortina Cortina Systems, Inc.
|
||||
|
10
MAINTAINERS
10
MAINTAINERS
@ -1612,6 +1612,16 @@ L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/net/ethernet/broadcom/bnx2x/
|
||||
|
||||
BROADCOM BCM2835 ARM ARCHICTURE
|
||||
M: Stephen Warren <swarren@wwwdotorg.org>
|
||||
L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-rpi.git
|
||||
S: Maintained
|
||||
F: arch/arm/mach-bcm2835/
|
||||
F: arch/arm/boot/dts/bcm2835*
|
||||
F: arch/arm/configs/bcm2835_defconfig
|
||||
F: drivers/*/*bcm2835*
|
||||
|
||||
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||
M: Matt Carlson <mcarlson@broadcom.com>
|
||||
M: Michael Chan <mchan@broadcom.com>
|
||||
|
@ -347,6 +347,23 @@ config ARCH_AT91
|
||||
This enables support for systems based on Atmel
|
||||
AT91RM9200 and AT91SAM9* processors.
|
||||
|
||||
config ARCH_BCM2835
|
||||
bool "Broadcom BCM2835 family"
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select ARM_AMBA
|
||||
select ARM_ERRATA_411920
|
||||
select ARM_TIMER_SP804
|
||||
select CLKDEV_LOOKUP
|
||||
select COMMON_CLK
|
||||
select CPU_V6
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select MULTI_IRQ_HANDLER
|
||||
select SPARSE_IRQ
|
||||
select USE_OF
|
||||
help
|
||||
This enables support for the Broadcom BCM2835 SoC. This SoC is
|
||||
use in the Raspberry Pi, and Roku 2 devices.
|
||||
|
||||
config ARCH_BCMRING
|
||||
bool "Broadcom BCMRING"
|
||||
depends on MMU
|
||||
@ -674,6 +691,7 @@ config ARCH_TEGRA
|
||||
select MIGHT_HAVE_CACHE_L2X0
|
||||
select ARCH_HAS_CPUFREQ
|
||||
select USE_OF
|
||||
select COMMON_CLK
|
||||
help
|
||||
This enables support for NVIDIA Tegra based systems (Tegra APX,
|
||||
Tegra 6xx and Tegra 2 series).
|
||||
|
@ -136,6 +136,7 @@ textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000
|
||||
# Machine directory name. This list is sorted alphanumerically
|
||||
# by CONFIG_* macro name.
|
||||
machine-$(CONFIG_ARCH_AT91) := at91
|
||||
machine-$(CONFIG_ARCH_BCM2835) := bcm2835
|
||||
machine-$(CONFIG_ARCH_BCMRING) := bcmring
|
||||
machine-$(CONFIG_ARCH_CLPS711X) := clps711x
|
||||
machine-$(CONFIG_ARCH_CNS3XXX) := cns3xxx
|
||||
|
12
arch/arm/boot/dts/bcm2835-rpi-b.dts
Normal file
12
arch/arm/boot/dts/bcm2835-rpi-b.dts
Normal file
@ -0,0 +1,12 @@
|
||||
/dts-v1/;
|
||||
/memreserve/ 0x0c000000 0x04000000;
|
||||
/include/ "bcm2835.dtsi"
|
||||
|
||||
/ {
|
||||
compatible = "raspberrypi,model-b", "brcm,bcm2835";
|
||||
model = "Raspberry Pi Model B";
|
||||
|
||||
memory {
|
||||
reg = <0 0x10000000>;
|
||||
};
|
||||
};
|
39
arch/arm/boot/dts/bcm2835.dtsi
Normal file
39
arch/arm/boot/dts/bcm2835.dtsi
Normal file
@ -0,0 +1,39 @@
|
||||
/include/ "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
compatible = "brcm,bcm2835";
|
||||
model = "BCM2835";
|
||||
interrupt-parent = <&intc>;
|
||||
|
||||
chosen {
|
||||
bootargs = "earlyprintk console=ttyAMA0";
|
||||
};
|
||||
|
||||
soc {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x7e000000 0x20000000 0x02000000>;
|
||||
|
||||
timer {
|
||||
compatible = "brcm,bcm2835-system-timer";
|
||||
reg = <0x7e003000 0x1000>;
|
||||
interrupts = <1 0>, <1 1>, <1 2>, <1 3>;
|
||||
clock-frequency = <1000000>;
|
||||
};
|
||||
|
||||
intc: interrupt-controller {
|
||||
compatible = "brcm,bcm2835-armctrl-ic";
|
||||
reg = <0x7e00b200 0x200>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
|
||||
uart@20201000 {
|
||||
compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
|
||||
reg = <0x7e201000 0x1000>;
|
||||
interrupts = <2 25>;
|
||||
clock-frequency = <3000000>;
|
||||
};
|
||||
};
|
||||
};
|
95
arch/arm/configs/bcm2835_defconfig
Normal file
95
arch/arm/configs/bcm2835_defconfig
Normal file
@ -0,0 +1,95 @@
|
||||
CONFIG_EXPERIMENTAL=y
|
||||
# CONFIG_LOCALVERSION_AUTO is not set
|
||||
CONFIG_SYSVIPC=y
|
||||
CONFIG_BSD_PROCESS_ACCT=y
|
||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
||||
CONFIG_FHANDLE=y
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_LOG_BUF_SHIFT=18
|
||||
CONFIG_CGROUP_FREEZER=y
|
||||
CONFIG_CGROUP_DEVICE=y
|
||||
CONFIG_CPUSETS=y
|
||||
CONFIG_CGROUP_CPUACCT=y
|
||||
CONFIG_RESOURCE_COUNTERS=y
|
||||
CONFIG_CGROUP_PERF=y
|
||||
CONFIG_CFS_BANDWIDTH=y
|
||||
CONFIG_RT_GROUP_SCHED=y
|
||||
CONFIG_NAMESPACES=y
|
||||
CONFIG_SCHED_AUTOGROUP=y
|
||||
CONFIG_RELAY=y
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
CONFIG_RD_BZIP2=y
|
||||
CONFIG_RD_LZMA=y
|
||||
CONFIG_RD_XZ=y
|
||||
CONFIG_RD_LZO=y
|
||||
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
|
||||
CONFIG_KALLSYMS_ALL=y
|
||||
CONFIG_EMBEDDED=y
|
||||
# CONFIG_COMPAT_BRK is not set
|
||||
CONFIG_PROFILING=y
|
||||
CONFIG_OPROFILE=y
|
||||
CONFIG_JUMP_LABEL=y
|
||||
# CONFIG_BLOCK is not set
|
||||
CONFIG_ARCH_BCM2835=y
|
||||
CONFIG_PREEMPT_VOLUNTARY=y
|
||||
CONFIG_AEABI=y
|
||||
CONFIG_COMPACTION=y
|
||||
CONFIG_KSM=y
|
||||
CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
|
||||
CONFIG_CLEANCACHE=y
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_CC_STACKPROTECTOR=y
|
||||
CONFIG_KEXEC=y
|
||||
CONFIG_CRASH_DUMP=y
|
||||
CONFIG_VFP=y
|
||||
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
|
||||
# CONFIG_SUSPEND is not set
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
# CONFIG_STANDALONE is not set
|
||||
# CONFIG_INPUT_MOUSEDEV is not set
|
||||
# CONFIG_INPUT_KEYBOARD is not set
|
||||
# CONFIG_INPUT_MOUSE is not set
|
||||
# CONFIG_SERIO is not set
|
||||
# CONFIG_VT is not set
|
||||
# CONFIG_UNIX98_PTYS is not set
|
||||
# CONFIG_LEGACY_PTYS is not set
|
||||
# CONFIG_DEVKMEM is not set
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
CONFIG_TTY_PRINTK=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
# CONFIG_HWMON is not set
|
||||
# CONFIG_USB_SUPPORT is not set
|
||||
# CONFIG_IOMMU_SUPPORT is not set
|
||||
# CONFIG_FILE_LOCKING is not set
|
||||
# CONFIG_DNOTIFY is not set
|
||||
# CONFIG_INOTIFY_USER is not set
|
||||
# CONFIG_PROC_FS is not set
|
||||
# CONFIG_SYSFS is not set
|
||||
# CONFIG_MISC_FILESYSTEMS is not set
|
||||
CONFIG_PRINTK_TIME=y
|
||||
# CONFIG_ENABLE_WARN_DEPRECATED is not set
|
||||
# CONFIG_ENABLE_MUST_CHECK is not set
|
||||
CONFIG_UNUSED_SYMBOLS=y
|
||||
CONFIG_LOCKUP_DETECTOR=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_MEMORY_INIT=y
|
||||
CONFIG_BOOT_PRINTK_DELAY=y
|
||||
CONFIG_SCHED_TRACER=y
|
||||
CONFIG_STACK_TRACER=y
|
||||
CONFIG_FUNCTION_PROFILER=y
|
||||
CONFIG_DYNAMIC_DEBUG=y
|
||||
CONFIG_KGDB=y
|
||||
CONFIG_KGDB_KDB=y
|
||||
CONFIG_TEST_KSTRTOX=y
|
||||
CONFIG_STRICT_DEVMEM=y
|
||||
CONFIG_DEBUG_LL=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
# CONFIG_XZ_DEC_X86 is not set
|
||||
# CONFIG_XZ_DEC_POWERPC is not set
|
||||
# CONFIG_XZ_DEC_IA64 is not set
|
||||
# CONFIG_XZ_DEC_ARM is not set
|
||||
# CONFIG_XZ_DEC_ARMTHUMB is not set
|
||||
# CONFIG_XZ_DEC_SPARC is not set
|
@ -8,6 +8,7 @@ CONFIG_LOG_BUF_SHIFT=16
|
||||
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
|
||||
CONFIG_SYSCTL_SYSCALL=y
|
||||
CONFIG_EMBEDDED=y
|
||||
CONFIG_PERF_EVENTS=y
|
||||
CONFIG_SLAB=y
|
||||
# CONFIG_BLK_DEV_BSG is not set
|
||||
# CONFIG_IOSCHED_DEADLINE is not set
|
||||
|
@ -14,6 +14,7 @@ CONFIG_NAMESPACES=y
|
||||
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
|
||||
CONFIG_SYSCTL_SYSCALL=y
|
||||
CONFIG_EMBEDDED=y
|
||||
CONFIG_PERF_EVENTS=y
|
||||
CONFIG_SLAB=y
|
||||
CONFIG_MODULES=y
|
||||
CONFIG_MODULE_FORCE_LOAD=y
|
||||
|
1
arch/arm/mach-bcm2835/Makefile
Normal file
1
arch/arm/mach-bcm2835/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-y += bcm2835.o
|
5
arch/arm/mach-bcm2835/Makefile.boot
Normal file
5
arch/arm/mach-bcm2835/Makefile.boot
Normal file
@ -0,0 +1,5 @@
|
||||
zreladdr-y := 0x00008000
|
||||
params_phys-y := 0x00000100
|
||||
initrd_phys-y := 0x00800000
|
||||
|
||||
dtb-y += bcm2835-rpi-b.dtb
|
64
arch/arm/mach-bcm2835/bcm2835.c
Normal file
64
arch/arm/mach-bcm2835/bcm2835.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/irqchip/bcm2835.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/bcm2835_timer.h>
|
||||
#include <linux/clk/bcm2835.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include <mach/bcm2835_soc.h>
|
||||
|
||||
static struct map_desc io_map __initdata = {
|
||||
.virtual = BCM2835_PERIPH_VIRT,
|
||||
.pfn = __phys_to_pfn(BCM2835_PERIPH_PHYS),
|
||||
.length = BCM2835_PERIPH_SIZE,
|
||||
.type = MT_DEVICE
|
||||
};
|
||||
|
||||
void __init bcm2835_map_io(void)
|
||||
{
|
||||
iotable_init(&io_map, 1);
|
||||
}
|
||||
|
||||
void __init bcm2835_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
bcm2835_init_clocks();
|
||||
|
||||
ret = of_platform_populate(NULL, of_default_bus_match_table, NULL,
|
||||
NULL);
|
||||
if (ret) {
|
||||
pr_err("of_platform_populate failed: %d\n", ret);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const bcm2835_compat[] = {
|
||||
"brcm,bcm2835",
|
||||
NULL
|
||||
};
|
||||
|
||||
DT_MACHINE_START(BCM2835, "BCM2835")
|
||||
.map_io = bcm2835_map_io,
|
||||
.init_irq = bcm2835_init_irq,
|
||||
.handle_irq = bcm2835_handle_irq,
|
||||
.init_machine = bcm2835_init,
|
||||
.timer = &bcm2835_timer,
|
||||
.dt_compat = bcm2835_compat
|
||||
MACHINE_END
|
29
arch/arm/mach-bcm2835/include/mach/bcm2835_soc.h
Normal file
29
arch/arm/mach-bcm2835/include/mach/bcm2835_soc.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Stephen Warren
|
||||
*
|
||||
* Derived from code:
|
||||
* Copyright (C) 2010 Broadcom
|
||||
*
|
||||
* 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 __MACH_BCM2835_BCM2835_SOC_H__
|
||||
#define __MACH_BCM2835_BCM2835_SOC_H__
|
||||
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#define BCM2835_PERIPH_PHYS 0x20000000
|
||||
#define BCM2835_PERIPH_VIRT 0xf0000000
|
||||
#define BCM2835_PERIPH_SIZE SZ_16M
|
||||
#define BCM2835_DEBUG_PHYS 0x20201000
|
||||
#define BCM2835_DEBUG_VIRT 0xf0201000
|
||||
|
||||
#endif
|
21
arch/arm/mach-bcm2835/include/mach/debug-macro.S
Normal file
21
arch/arm/mach-bcm2835/include/mach/debug-macro.S
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Debugging macro include header
|
||||
*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
* Copyright (C) 1994-1999 Russell King
|
||||
* Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
|
||||
*
|
||||
* 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 <mach/bcm2835_soc.h>
|
||||
|
||||
.macro addruart, rp, rv, tmp
|
||||
ldr \rp, =BCM2835_DEBUG_PHYS
|
||||
ldr \rv, =BCM2835_DEBUG_VIRT
|
||||
.endm
|
||||
|
||||
#include <asm/hardware/debug-pl01x.S>
|
26
arch/arm/mach-bcm2835/include/mach/timex.h
Normal file
26
arch/arm/mach-bcm2835/include/mach/timex.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* BCM2835 system clock frequency
|
||||
*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_TIMEX_H
|
||||
#define __ASM_ARCH_TIMEX_H
|
||||
|
||||
#define CLOCK_TICK_RATE (1000000)
|
||||
|
||||
#endif
|
45
arch/arm/mach-bcm2835/include/mach/uncompress.h
Normal file
45
arch/arm/mach-bcm2835/include/mach/uncompress.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
* Copyright (C) 2003 ARM Limited
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/amba/serial.h>
|
||||
#include <mach/bcm2835_soc.h>
|
||||
|
||||
#define UART0_BASE BCM2835_DEBUG_PHYS
|
||||
|
||||
#define BCM2835_UART_DR IOMEM(UART0_BASE + UART01x_DR)
|
||||
#define BCM2835_UART_FR IOMEM(UART0_BASE + UART01x_FR)
|
||||
#define BCM2835_UART_CR IOMEM(UART0_BASE + UART011_CR)
|
||||
|
||||
static inline void putc(int c)
|
||||
{
|
||||
while (__raw_readl(BCM2835_UART_FR) & UART01x_FR_TXFF)
|
||||
barrier();
|
||||
|
||||
__raw_writel(c, BCM2835_UART_DR);
|
||||
}
|
||||
|
||||
static inline void flush(void)
|
||||
{
|
||||
int fr;
|
||||
|
||||
do {
|
||||
fr = __raw_readl(BCM2835_UART_FR);
|
||||
barrier();
|
||||
} while ((fr & (UART011_FR_TXFE | UART01x_FR_BUSY)) != UART011_FR_TXFE);
|
||||
}
|
||||
|
||||
#define arch_decomp_setup()
|
||||
#define arch_decomp_wdog()
|
@ -62,8 +62,8 @@ enum mx35_clks {
|
||||
kpp_gate, mlb_gate, mshc_gate, owire_gate, pwm_gate, rngc_gate,
|
||||
rtc_gate, rtic_gate, scc_gate, sdma_gate, spba_gate, spdif_gate,
|
||||
ssi1_gate, ssi2_gate, uart1_gate, uart2_gate, uart3_gate, usbotg_gate,
|
||||
wdog_gate, max_gate, admux_gate, csi_gate, iim_gate, gpu2d_gate,
|
||||
clk_max
|
||||
wdog_gate, max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate,
|
||||
gpu2d_gate, clk_max
|
||||
};
|
||||
|
||||
static struct clk *clk[clk_max];
|
||||
@ -142,6 +142,9 @@ int __init mx35_clocks_init()
|
||||
|
||||
clk[nfc_div] = imx_clk_divider("nfc_div", "ahb", base + MX35_CCM_PDR4, 28, 4);
|
||||
|
||||
clk[csi_sel] = imx_clk_mux("csi_sel", base + MX35_CCM_PDR2, 7, 1, std_sel, ARRAY_SIZE(std_sel));
|
||||
clk[csi_div] = imx_clk_divider("csi_div", "csi_sel", base + MX35_CCM_PDR2, 16, 6);
|
||||
|
||||
clk[asrc_gate] = imx_clk_gate2("asrc_gate", "ipg", base + MX35_CCM_CGR0, 0);
|
||||
clk[pata_gate] = imx_clk_gate2("pata_gate", "ipg", base + MX35_CCM_CGR0, 2);
|
||||
clk[audmux_gate] = imx_clk_gate2("audmux_gate", "ipg", base + MX35_CCM_CGR0, 4);
|
||||
@ -192,7 +195,7 @@ int __init mx35_clocks_init()
|
||||
clk[max_gate] = imx_clk_gate2("max_gate", "dummy", base + MX35_CCM_CGR2, 26);
|
||||
clk[admux_gate] = imx_clk_gate2("admux_gate", "ipg", base + MX35_CCM_CGR2, 30);
|
||||
|
||||
clk[csi_gate] = imx_clk_gate2("csi_gate", "ipg", base + MX35_CCM_CGR3, 0);
|
||||
clk[csi_gate] = imx_clk_gate2("csi_gate", "csi_div", base + MX35_CCM_CGR3, 0);
|
||||
clk[iim_gate] = imx_clk_gate2("iim_gate", "ipg", base + MX35_CCM_CGR3, 2);
|
||||
clk[gpu2d_gate] = imx_clk_gate2("gpu2d_gate", "ahb", base + MX35_CCM_CGR3, 4);
|
||||
|
||||
@ -228,6 +231,7 @@ int __init mx35_clocks_init()
|
||||
clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2");
|
||||
clk_register_clkdev(clk[ipu_gate], NULL, "ipu-core");
|
||||
clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb");
|
||||
clk_register_clkdev(clk[kpp_gate], NULL, "imx-keypad");
|
||||
clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1");
|
||||
clk_register_clkdev(clk[sdma_gate], NULL, "imx35-sdma");
|
||||
clk_register_clkdev(clk[ssi1_gate], NULL, "imx-ssi.0");
|
||||
@ -253,6 +257,7 @@ int __init mx35_clocks_init()
|
||||
clk_register_clkdev(clk[usbotg_gate], "ahb", "fsl-usb2-udc");
|
||||
clk_register_clkdev(clk[wdog_gate], NULL, "imx2-wdt.0");
|
||||
clk_register_clkdev(clk[nfc_div], NULL, "mxc_nand.0");
|
||||
clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0");
|
||||
|
||||
clk_prepare_enable(clk[spba_gate]);
|
||||
clk_prepare_enable(clk[gpio1_gate]);
|
||||
|
@ -49,6 +49,7 @@ static const char *mx53_tve_ext_sel[] = { "pll4_sw", "ckih1", };
|
||||
static const char *tve_sel[] = { "tve_pred", "tve_ext_sel", };
|
||||
static const char *ipu_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb", };
|
||||
static const char *vpu_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb", };
|
||||
static const char *mx53_can_sel[] = { "ipg", "ckih1", "ckih2", "lp_apm", };
|
||||
|
||||
enum imx5_clks {
|
||||
dummy, ckil, osc, ckih1, ckih2, ahb, ipg, axi_a, axi_b, uart_pred,
|
||||
@ -82,6 +83,7 @@ enum imx5_clks {
|
||||
ssi_ext1_podf, ssi_ext2_pred, ssi_ext2_podf, ssi1_root_gate,
|
||||
ssi2_root_gate, ssi3_root_gate, ssi_ext1_gate, ssi_ext2_gate,
|
||||
epit1_ipg_gate, epit1_hf_gate, epit2_ipg_gate, epit2_hf_gate,
|
||||
can_sel, can1_serial_gate, can1_ipg_gate,
|
||||
clk_max
|
||||
};
|
||||
|
||||
@ -421,8 +423,12 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
|
||||
clk[esdhc4_per_gate] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14);
|
||||
clk[usb_phy1_gate] = imx_clk_gate2("usb_phy1_gate", "usb_phy_sel", MXC_CCM_CCGR4, 10);
|
||||
clk[usb_phy2_gate] = imx_clk_gate2("usb_phy2_gate", "usb_phy_sel", MXC_CCM_CCGR4, 12);
|
||||
clk[can2_serial_gate] = imx_clk_gate2("can2_serial_gate", "ipg", MXC_CCM_CCGR4, 6);
|
||||
clk[can2_ipg_gate] = imx_clk_gate2("can2_ipg_gate", "ipg", MXC_CCM_CCGR4, 8);
|
||||
clk[can_sel] = imx_clk_mux("can_sel", MXC_CCM_CSCMR2, 6, 2,
|
||||
mx53_can_sel, ARRAY_SIZE(mx53_can_sel));
|
||||
clk[can1_serial_gate] = imx_clk_gate2("can1_serial_gate", "can_sel", MXC_CCM_CCGR6, 22);
|
||||
clk[can1_ipg_gate] = imx_clk_gate2("can1_ipg_gate", "ipg", MXC_CCM_CCGR6, 20);
|
||||
clk[can2_serial_gate] = imx_clk_gate2("can2_serial_gate", "can_sel", MXC_CCM_CCGR4, 8);
|
||||
clk[can2_ipg_gate] = imx_clk_gate2("can2_ipg_gate", "ipg", MXC_CCM_CCGR4, 6);
|
||||
clk[i2c3_gate] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clk); i++)
|
||||
@ -455,6 +461,10 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
|
||||
clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "63fcc000.ssi");
|
||||
clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "50014000.ssi");
|
||||
clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "63fd0000.ssi");
|
||||
clk_register_clkdev(clk[can1_ipg_gate], "ipg", "53fc8000.can");
|
||||
clk_register_clkdev(clk[can1_serial_gate], "per", "53fc8000.can");
|
||||
clk_register_clkdev(clk[can2_ipg_gate], "ipg", "53fcc000.can");
|
||||
clk_register_clkdev(clk[can2_serial_gate], "per", "53fcc000.can");
|
||||
|
||||
/* set SDHC root clock to 200MHZ*/
|
||||
clk_set_rate(clk[esdhc_a_podf], 200000000);
|
||||
|
@ -283,21 +283,25 @@ static int lpc32xx_set_irq_type(struct irq_data *d, unsigned int type)
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
/* Rising edge sensitive */
|
||||
__lpc32xx_set_irq_type(d->hwirq, 1, 1);
|
||||
__irq_set_handler_locked(d->hwirq, handle_edge_irq);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
/* Falling edge sensitive */
|
||||
__lpc32xx_set_irq_type(d->hwirq, 0, 1);
|
||||
__irq_set_handler_locked(d->hwirq, handle_edge_irq);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
/* Low level sensitive */
|
||||
__lpc32xx_set_irq_type(d->hwirq, 0, 0);
|
||||
__irq_set_handler_locked(d->hwirq, handle_level_irq);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
/* High level sensitive */
|
||||
__lpc32xx_set_irq_type(d->hwirq, 1, 0);
|
||||
__irq_set_handler_locked(d->hwirq, handle_level_irq);
|
||||
break;
|
||||
|
||||
/* Other modes are not supported */
|
||||
@ -305,9 +309,6 @@ static int lpc32xx_set_irq_type(struct irq_data *d, unsigned int type)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Ok to use the level handler for all types */
|
||||
irq_set_handler(d->hwirq, handle_level_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,8 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mtd/lpc32xx_slc.h>
|
||||
#include <linux/mtd/lpc32xx_mlc.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/mach-types.h>
|
||||
@ -223,6 +225,14 @@ static struct mmci_platform_data lpc32xx_mmci_data = {
|
||||
* gather, and the MMCI driver doesn't do it this way */
|
||||
};
|
||||
|
||||
static struct lpc32xx_slc_platform_data lpc32xx_slc_data = {
|
||||
.dma_filter = pl08x_filter_id,
|
||||
};
|
||||
|
||||
static struct lpc32xx_mlc_platform_data lpc32xx_mlc_data = {
|
||||
.dma_filter = pl08x_filter_id,
|
||||
};
|
||||
|
||||
static const struct of_dev_auxdata lpc32xx_auxdata_lookup[] __initconst = {
|
||||
OF_DEV_AUXDATA("arm,pl022", 0x20084000, "dev:ssp0", &lpc32xx_ssp0_data),
|
||||
OF_DEV_AUXDATA("arm,pl022", 0x2008C000, "dev:ssp1", &lpc32xx_ssp1_data),
|
||||
@ -230,6 +240,10 @@ static const struct of_dev_auxdata lpc32xx_auxdata_lookup[] __initconst = {
|
||||
OF_DEV_AUXDATA("arm,pl080", 0x31000000, "pl08xdmac", &pl08x_pd),
|
||||
OF_DEV_AUXDATA("arm,pl18x", 0x20098000, "20098000.sd",
|
||||
&lpc32xx_mmci_data),
|
||||
OF_DEV_AUXDATA("nxp,lpc3220-slc", 0x20020000, "20020000.flash",
|
||||
&lpc32xx_slc_data),
|
||||
OF_DEV_AUXDATA("nxp,lpc3220-mlc", 0x200a8000, "200a8000.flash",
|
||||
&lpc32xx_mlc_data),
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -253,12 +267,6 @@ static void __init lpc3250_machine_init(void)
|
||||
|
||||
of_platform_populate(NULL, of_default_bus_match_table,
|
||||
lpc32xx_auxdata_lookup, NULL);
|
||||
|
||||
/* Register GPIOs used on this board */
|
||||
if (gpio_request(MMC_PWR_ENABLE_GPIO, "mmc_power_en"))
|
||||
pr_err("Error requesting gpio %u", MMC_PWR_ENABLE_GPIO);
|
||||
else if (gpio_direction_output(MMC_PWR_ENABLE_GPIO, 1))
|
||||
pr_err("Error setting gpio %u to output", MMC_PWR_ENABLE_GPIO);
|
||||
}
|
||||
|
||||
static char const *lpc32xx_dt_compat[] __initdata = {
|
||||
|
@ -174,6 +174,7 @@ obj-$(CONFIG_SOC_OMAP2430) += omap_hwmod_2430_data.o
|
||||
obj-$(CONFIG_ARCH_OMAP3) += omap_hwmod_2xxx_3xxx_ipblock_data.o
|
||||
obj-$(CONFIG_ARCH_OMAP3) += omap_hwmod_2xxx_3xxx_interconnect_data.o
|
||||
obj-$(CONFIG_ARCH_OMAP3) += omap_hwmod_3xxx_data.o
|
||||
obj-$(CONFIG_SOC_AM33XX) += omap_hwmod_33xx_data.o
|
||||
obj-$(CONFIG_ARCH_OMAP4) += omap_hwmod_44xx_data.o
|
||||
|
||||
# EMU peripherals
|
||||
|
@ -211,7 +211,7 @@ void omap2_init_dpll_parent(struct clk *clk)
|
||||
if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
|
||||
v == OMAP3XXX_EN_DPLL_FRBYPASS)
|
||||
clk_reparent(clk, dd->clk_bypass);
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
} else if (soc_is_am33xx() || cpu_is_omap44xx()) {
|
||||
if (v == OMAP4XXX_EN_DPLL_LPBYPASS ||
|
||||
v == OMAP4XXX_EN_DPLL_FRBYPASS ||
|
||||
v == OMAP4XXX_EN_DPLL_MNBYPASS)
|
||||
@ -257,7 +257,7 @@ u32 omap2_get_dpll_rate(struct clk *clk)
|
||||
if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
|
||||
v == OMAP3XXX_EN_DPLL_FRBYPASS)
|
||||
return dd->clk_bypass->rate;
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
} else if (soc_is_am33xx() || cpu_is_omap44xx()) {
|
||||
if (v == OMAP4XXX_EN_DPLL_LPBYPASS ||
|
||||
v == OMAP4XXX_EN_DPLL_FRBYPASS ||
|
||||
v == OMAP4XXX_EN_DPLL_MNBYPASS)
|
||||
|
@ -1027,7 +1027,9 @@ static struct omap_clk am33xx_clks[] = {
|
||||
CLK(NULL, "cefuse_fck", &cefuse_fck, CK_AM33XX),
|
||||
CLK(NULL, "clkdiv32k_ick", &clkdiv32k_ick, CK_AM33XX),
|
||||
CLK(NULL, "dcan0_fck", &dcan0_fck, CK_AM33XX),
|
||||
CLK("481cc000.d_can", NULL, &dcan0_fck, CK_AM33XX),
|
||||
CLK(NULL, "dcan1_fck", &dcan1_fck, CK_AM33XX),
|
||||
CLK("481d0000.d_can", NULL, &dcan1_fck, CK_AM33XX),
|
||||
CLK(NULL, "debugss_ick", &debugss_ick, CK_AM33XX),
|
||||
CLK(NULL, "pruss_ocp_gclk", &pruss_ocp_gclk, CK_AM33XX),
|
||||
CLK("davinci-mcasp.0", NULL, &mcasp0_fck, CK_AM33XX),
|
||||
|
@ -311,7 +311,7 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
|
||||
* Set jitter correction. No jitter correction for OMAP4 and 3630
|
||||
* since freqsel field is no longer present
|
||||
*/
|
||||
if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
|
||||
if (!soc_is_am33xx() && !cpu_is_omap44xx() && !cpu_is_omap3630()) {
|
||||
v = __raw_readl(dd->control_reg);
|
||||
v &= ~dd->freqsel_mask;
|
||||
v |= freqsel << __ffs(dd->freqsel_mask);
|
||||
@ -471,7 +471,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* No freqsel on OMAP4 and OMAP3630 */
|
||||
if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
|
||||
if (!soc_is_am33xx() && !cpu_is_omap44xx() && !cpu_is_omap3630()) {
|
||||
freqsel = _omap3_dpll_compute_freqsel(clk,
|
||||
dd->last_rounded_n);
|
||||
if (!freqsel)
|
||||
|
@ -524,6 +524,8 @@ void __init am33xx_init_early(void)
|
||||
am33xx_voltagedomains_init();
|
||||
am33xx_powerdomains_init();
|
||||
am33xx_clockdomains_init();
|
||||
am33xx_hwmod_init();
|
||||
omap_hwmod_init_postsetup();
|
||||
am33xx_clk_init();
|
||||
}
|
||||
#endif
|
||||
|
@ -149,8 +149,10 @@
|
||||
#include "powerdomain.h"
|
||||
#include "cm2xxx_3xxx.h"
|
||||
#include "cminst44xx.h"
|
||||
#include "cm33xx.h"
|
||||
#include "prm2xxx_3xxx.h"
|
||||
#include "prm44xx.h"
|
||||
#include "prm33xx.h"
|
||||
#include "prminst44xx.h"
|
||||
#include "mux.h"
|
||||
#include "pm.h"
|
||||
@ -867,6 +869,26 @@ static void _omap4_enable_module(struct omap_hwmod *oh)
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_enable_module - enable CLKCTRL modulemode on AM33XX
|
||||
* @oh: struct omap_hwmod *
|
||||
*
|
||||
* Enables the PRCM module mode related to the hwmod @oh.
|
||||
* No return value.
|
||||
*/
|
||||
static void _am33xx_enable_module(struct omap_hwmod *oh)
|
||||
{
|
||||
if (!oh->clkdm || !oh->prcm.omap4.modulemode)
|
||||
return;
|
||||
|
||||
pr_debug("omap_hwmod: %s: %s: %d\n",
|
||||
oh->name, __func__, oh->prcm.omap4.modulemode);
|
||||
|
||||
am33xx_cm_module_enable(oh->prcm.omap4.modulemode, oh->clkdm->cm_inst,
|
||||
oh->clkdm->clkdm_offs,
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _omap4_wait_target_disable - wait for a module to be disabled on OMAP4
|
||||
* @oh: struct omap_hwmod *
|
||||
@ -893,6 +915,31 @@ static int _omap4_wait_target_disable(struct omap_hwmod *oh)
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_wait_target_disable - wait for a module to be disabled on AM33XX
|
||||
* @oh: struct omap_hwmod *
|
||||
*
|
||||
* Wait for a module @oh to enter slave idle. Returns 0 if the module
|
||||
* does not have an IDLEST bit or if the module successfully enters
|
||||
* slave idle; otherwise, pass along the return value of the
|
||||
* appropriate *_cm*_wait_module_idle() function.
|
||||
*/
|
||||
static int _am33xx_wait_target_disable(struct omap_hwmod *oh)
|
||||
{
|
||||
if (!oh)
|
||||
return -EINVAL;
|
||||
|
||||
if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
|
||||
return 0;
|
||||
|
||||
if (oh->flags & HWMOD_NO_IDLEST)
|
||||
return 0;
|
||||
|
||||
return am33xx_cm_wait_module_idle(oh->clkdm->cm_inst,
|
||||
oh->clkdm->clkdm_offs,
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh
|
||||
* @oh: struct omap_hwmod *oh
|
||||
@ -1613,6 +1660,36 @@ static int _omap4_disable_module(struct omap_hwmod *oh)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_disable_module - enable CLKCTRL modulemode on AM33XX
|
||||
* @oh: struct omap_hwmod *
|
||||
*
|
||||
* Disable the PRCM module mode related to the hwmod @oh.
|
||||
* Return EINVAL if the modulemode is not supported and 0 in case of success.
|
||||
*/
|
||||
static int _am33xx_disable_module(struct omap_hwmod *oh)
|
||||
{
|
||||
int v;
|
||||
|
||||
if (!oh->clkdm || !oh->prcm.omap4.modulemode)
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__);
|
||||
|
||||
am33xx_cm_module_disable(oh->clkdm->cm_inst, oh->clkdm->clkdm_offs,
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
|
||||
if (_are_any_hardreset_lines_asserted(oh))
|
||||
return 0;
|
||||
|
||||
v = _am33xx_wait_target_disable(oh);
|
||||
if (v)
|
||||
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
|
||||
oh->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _ocp_softreset - reset an omap_hwmod via the OCP_SYSCONFIG bit
|
||||
* @oh: struct omap_hwmod *
|
||||
@ -2548,6 +2625,33 @@ static int _omap4_wait_target_ready(struct omap_hwmod *oh)
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_wait_target_ready - wait for a module to leave slave idle
|
||||
* @oh: struct omap_hwmod *
|
||||
*
|
||||
* Wait for a module @oh to leave slave idle. Returns 0 if the module
|
||||
* does not have an IDLEST bit or if the module successfully leaves
|
||||
* slave idle; otherwise, pass along the return value of the
|
||||
* appropriate *_cm*_wait_module_ready() function.
|
||||
*/
|
||||
static int _am33xx_wait_target_ready(struct omap_hwmod *oh)
|
||||
{
|
||||
if (!oh || !oh->clkdm)
|
||||
return -EINVAL;
|
||||
|
||||
if (oh->flags & HWMOD_NO_IDLEST)
|
||||
return 0;
|
||||
|
||||
if (!_find_mpu_rt_port(oh))
|
||||
return 0;
|
||||
|
||||
/* XXX check module SIDLEMODE, hardreset status */
|
||||
|
||||
return am33xx_cm_wait_module_ready(oh->clkdm->cm_inst,
|
||||
oh->clkdm->clkdm_offs,
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _omap2_assert_hardreset - call OMAP2 PRM hardreset fn with hwmod args
|
||||
* @oh: struct omap_hwmod * to assert hardreset
|
||||
@ -2679,6 +2783,72 @@ static int _omap4_is_hardreset_asserted(struct omap_hwmod *oh,
|
||||
oh->prcm.omap4.rstctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_assert_hardreset - call AM33XX PRM hardreset fn with hwmod args
|
||||
* @oh: struct omap_hwmod * to assert hardreset
|
||||
* @ohri: hardreset line data
|
||||
*
|
||||
* Call am33xx_prminst_assert_hardreset() with parameters extracted
|
||||
* from the hwmod @oh and the hardreset line data @ohri. Only
|
||||
* intended for use as an soc_ops function pointer. Passes along the
|
||||
* return value from am33xx_prminst_assert_hardreset(). XXX This
|
||||
* function is scheduled for removal when the PRM code is moved into
|
||||
* drivers/.
|
||||
*/
|
||||
static int _am33xx_assert_hardreset(struct omap_hwmod *oh,
|
||||
struct omap_hwmod_rst_info *ohri)
|
||||
|
||||
{
|
||||
return am33xx_prm_assert_hardreset(ohri->rst_shift,
|
||||
oh->clkdm->pwrdm.ptr->prcm_offs,
|
||||
oh->prcm.omap4.rstctrl_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_deassert_hardreset - call AM33XX PRM hardreset fn with hwmod args
|
||||
* @oh: struct omap_hwmod * to deassert hardreset
|
||||
* @ohri: hardreset line data
|
||||
*
|
||||
* Call am33xx_prminst_deassert_hardreset() with parameters extracted
|
||||
* from the hwmod @oh and the hardreset line data @ohri. Only
|
||||
* intended for use as an soc_ops function pointer. Passes along the
|
||||
* return value from am33xx_prminst_deassert_hardreset(). XXX This
|
||||
* function is scheduled for removal when the PRM code is moved into
|
||||
* drivers/.
|
||||
*/
|
||||
static int _am33xx_deassert_hardreset(struct omap_hwmod *oh,
|
||||
struct omap_hwmod_rst_info *ohri)
|
||||
{
|
||||
if (ohri->st_shift)
|
||||
pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
|
||||
oh->name, ohri->name);
|
||||
|
||||
return am33xx_prm_deassert_hardreset(ohri->rst_shift,
|
||||
oh->clkdm->pwrdm.ptr->prcm_offs,
|
||||
oh->prcm.omap4.rstctrl_offs,
|
||||
oh->prcm.omap4.rstst_offs);
|
||||
}
|
||||
|
||||
/**
|
||||
* _am33xx_is_hardreset_asserted - call AM33XX PRM hardreset fn with hwmod args
|
||||
* @oh: struct omap_hwmod * to test hardreset
|
||||
* @ohri: hardreset line data
|
||||
*
|
||||
* Call am33xx_prminst_is_hardreset_asserted() with parameters
|
||||
* extracted from the hwmod @oh and the hardreset line data @ohri.
|
||||
* Only intended for use as an soc_ops function pointer. Passes along
|
||||
* the return value from am33xx_prminst_is_hardreset_asserted(). XXX
|
||||
* This function is scheduled for removal when the PRM code is moved
|
||||
* into drivers/.
|
||||
*/
|
||||
static int _am33xx_is_hardreset_asserted(struct omap_hwmod *oh,
|
||||
struct omap_hwmod_rst_info *ohri)
|
||||
{
|
||||
return am33xx_prm_is_hardreset_asserted(ohri->rst_shift,
|
||||
oh->clkdm->pwrdm.ptr->prcm_offs,
|
||||
oh->prcm.omap4.rstctrl_offs);
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
|
||||
u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs)
|
||||
@ -3705,6 +3875,14 @@ void __init omap_hwmod_init(void)
|
||||
soc_ops.deassert_hardreset = _omap4_deassert_hardreset;
|
||||
soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted;
|
||||
soc_ops.init_clkdm = _init_clkdm;
|
||||
} else if (soc_is_am33xx()) {
|
||||
soc_ops.enable_module = _am33xx_enable_module;
|
||||
soc_ops.disable_module = _am33xx_disable_module;
|
||||
soc_ops.wait_target_ready = _am33xx_wait_target_ready;
|
||||
soc_ops.assert_hardreset = _am33xx_assert_hardreset;
|
||||
soc_ops.deassert_hardreset = _am33xx_deassert_hardreset;
|
||||
soc_ops.is_hardreset_asserted = _am33xx_is_hardreset_asserted;
|
||||
soc_ops.init_clkdm = _init_clkdm;
|
||||
} else {
|
||||
WARN(1, "omap_hwmod: unknown SoC type\n");
|
||||
}
|
||||
|
3381
arch/arm/mach-omap2/omap_hwmod_33xx_data.c
Normal file
3381
arch/arm/mach-omap2/omap_hwmod_33xx_data.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -763,6 +763,13 @@ static void __init kzm_init(void)
|
||||
platform_add_devices(kzm_devices, ARRAY_SIZE(kzm_devices));
|
||||
}
|
||||
|
||||
static void kzm9g_restart(char mode, const char *cmd)
|
||||
{
|
||||
#define RESCNT2 0xe6188020
|
||||
/* Do soft power on reset */
|
||||
writel((1 << 31), RESCNT2);
|
||||
}
|
||||
|
||||
static const char *kzm9g_boards_compat_dt[] __initdata = {
|
||||
"renesas,kzm9g",
|
||||
NULL,
|
||||
@ -777,5 +784,6 @@ DT_MACHINE_START(KZM9G_DT, "kzm9g")
|
||||
.init_machine = kzm_init,
|
||||
.init_late = shmobile_init_late,
|
||||
.timer = &shmobile_timer,
|
||||
.restart = kzm9g_restart,
|
||||
.dt_compat = kzm9g_boards_compat_dt,
|
||||
MACHINE_END
|
||||
|
@ -86,11 +86,16 @@ static struct clk div4_clks[DIV4_NR] = {
|
||||
0x0300, CLK_ENABLE_ON_INIT),
|
||||
};
|
||||
|
||||
enum { MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021,
|
||||
enum { MSTP323, MSTP322, MSTP321, MSTP320,
|
||||
MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021,
|
||||
MSTP016, MSTP015, MSTP014,
|
||||
MSTP_NR };
|
||||
|
||||
static struct clk mstp_clks[MSTP_NR] = {
|
||||
[MSTP323] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 23, 0), /* SDHI0 */
|
||||
[MSTP322] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 22, 0), /* SDHI1 */
|
||||
[MSTP321] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 21, 0), /* SDHI2 */
|
||||
[MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 20, 0), /* SDHI3 */
|
||||
[MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), /* SCIF0 */
|
||||
[MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), /* SCIF1 */
|
||||
[MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), /* SCIF2 */
|
||||
@ -149,6 +154,10 @@ static struct clk_lookup lookups[] = {
|
||||
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_mobile_sdhi.0", &mstp_clks[MSTP323]), /* SDHI0 */
|
||||
CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP322]), /* SDHI1 */
|
||||
CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP321]), /* SDHI2 */
|
||||
CLKDEV_DEV_ID("sh_mobile_sdhi.3", &mstp_clks[MSTP320]), /* SDHI3 */
|
||||
};
|
||||
|
||||
void __init r8a7779_clock_init(void)
|
||||
|
@ -356,6 +356,26 @@ static struct platform_device gio4_device = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource pmu_resources[] = {
|
||||
[0] = {
|
||||
.start = 152,
|
||||
.end = 152,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[1] = {
|
||||
.start = 153,
|
||||
.end = 153,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device pmu_device = {
|
||||
.name = "arm-pmu",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(pmu_resources),
|
||||
.resource = pmu_resources,
|
||||
};
|
||||
|
||||
static struct platform_device *emev2_early_devices[] __initdata = {
|
||||
&uart0_device,
|
||||
&uart1_device,
|
||||
@ -370,6 +390,7 @@ static struct platform_device *emev2_late_devices[] __initdata = {
|
||||
&gio2_device,
|
||||
&gio3_device,
|
||||
&gio4_device,
|
||||
&pmu_device,
|
||||
};
|
||||
|
||||
void __init emev2_add_standard_devices(void)
|
||||
|
@ -734,6 +734,26 @@ static struct platform_device mpdma0_device = {
|
||||
},
|
||||
};
|
||||
|
||||
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 = "arm-pmu",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(pmu_resources),
|
||||
.resource = pmu_resources,
|
||||
};
|
||||
|
||||
static struct platform_device *sh73a0_early_devices[] __initdata = {
|
||||
&scif0_device,
|
||||
&scif1_device,
|
||||
@ -757,6 +777,7 @@ static struct platform_device *sh73a0_late_devices[] __initdata = {
|
||||
&i2c4_device,
|
||||
&dma0_device,
|
||||
&mpdma0_device,
|
||||
&pmu_device,
|
||||
};
|
||||
|
||||
#define SRCR2 0xe61580b0
|
||||
|
@ -12,9 +12,13 @@ obj-y += powergate.o
|
||||
obj-y += apbio.o
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
||||
obj-$(CONFIG_CPU_IDLE) += sleep.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-t20.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-t30.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
obj-$(CONFIG_SMP) += reset.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
|
@ -70,6 +70,7 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
|
||||
|
||||
static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
|
||||
/* name parent rate enabled */
|
||||
{ "uarta", "pll_p", 216000000, true },
|
||||
{ "uartd", "pll_p", 216000000, true },
|
||||
{ "usbd", "clk_m", 12000000, false },
|
||||
{ "usb2", "clk_m", 12000000, false },
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
@ -19,8 +20,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
@ -32,44 +31,38 @@
|
||||
|
||||
#include "board.h"
|
||||
#include "clock.h"
|
||||
#include "tegra_cpu_car.h"
|
||||
|
||||
/* Global data of Tegra CPU CAR ops */
|
||||
struct tegra_cpu_car_ops *tegra_cpu_car_ops;
|
||||
|
||||
/*
|
||||
* Locking:
|
||||
*
|
||||
* Each struct clk has a spinlock.
|
||||
*
|
||||
* To avoid AB-BA locking problems, locks must always be traversed from child
|
||||
* clock to parent clock. For example, when enabling a clock, the clock's lock
|
||||
* is taken, and then clk_enable is called on the parent, which take's the
|
||||
* parent clock's lock. There is one exceptions to this ordering: When dumping
|
||||
* the clock tree through debugfs. In this case, clk_lock_all is called,
|
||||
* which attemps to iterate through the entire list of clocks and take every
|
||||
* clock lock. If any call to spin_trylock fails, all locked clocks are
|
||||
* unlocked, and the process is retried. When all the locks are held,
|
||||
* the only clock operation that can be called is clk_get_rate_all_locked.
|
||||
*
|
||||
* Within a single clock, no clock operation can call another clock operation
|
||||
* on itself, except for clk_get_rate_locked and clk_set_rate_locked. Any
|
||||
* clock operation can call any other clock operation on any of it's possible
|
||||
* parents.
|
||||
*
|
||||
* An additional mutex, clock_list_lock, is used to protect the list of all
|
||||
* clocks.
|
||||
*
|
||||
* The clock operations must lock internally to protect against
|
||||
* read-modify-write on registers that are shared by multiple clocks
|
||||
*/
|
||||
static DEFINE_MUTEX(clock_list_lock);
|
||||
static LIST_HEAD(clocks);
|
||||
|
||||
void tegra_clk_add(struct clk *clk)
|
||||
{
|
||||
struct clk_tegra *c = to_clk_tegra(__clk_get_hw(clk));
|
||||
|
||||
mutex_lock(&clock_list_lock);
|
||||
list_add(&c->node, &clocks);
|
||||
mutex_unlock(&clock_list_lock);
|
||||
}
|
||||
|
||||
struct clk *tegra_get_clock_by_name(const char *name)
|
||||
{
|
||||
struct clk *c;
|
||||
struct clk_tegra *c;
|
||||
struct clk *ret = NULL;
|
||||
mutex_lock(&clock_list_lock);
|
||||
list_for_each_entry(c, &clocks, node) {
|
||||
if (strcmp(c->name, name) == 0) {
|
||||
ret = c;
|
||||
if (strcmp(__clk_get_name(c->hw.clk), name) == 0) {
|
||||
ret = c->hw.clk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -77,280 +70,36 @@ struct clk *tegra_get_clock_by_name(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Must be called with c->spinlock held */
|
||||
static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
|
||||
{
|
||||
u64 rate;
|
||||
|
||||
rate = clk_get_rate(p);
|
||||
|
||||
if (c->mul != 0 && c->div != 0) {
|
||||
rate *= c->mul;
|
||||
rate += c->div - 1; /* round up */
|
||||
do_div(rate, c->div);
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* Must be called with c->spinlock held */
|
||||
unsigned long clk_get_rate_locked(struct clk *c)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (c->parent)
|
||||
rate = clk_predict_rate_from_parent(c, c->parent);
|
||||
else
|
||||
rate = c->rate;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
unsigned long clk_get_rate(struct clk *c)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long rate;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
rate = clk_get_rate_locked(c);
|
||||
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
|
||||
return rate;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_rate);
|
||||
|
||||
int clk_reparent(struct clk *c, struct clk *parent)
|
||||
{
|
||||
c->parent = parent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clk_init(struct clk *c)
|
||||
{
|
||||
spin_lock_init(&c->spinlock);
|
||||
|
||||
if (c->ops && c->ops->init)
|
||||
c->ops->init(c);
|
||||
|
||||
if (!c->ops || !c->ops->enable) {
|
||||
c->refcnt++;
|
||||
c->set = true;
|
||||
if (c->parent)
|
||||
c->state = c->parent->state;
|
||||
else
|
||||
c->state = ON;
|
||||
}
|
||||
|
||||
mutex_lock(&clock_list_lock);
|
||||
list_add(&c->node, &clocks);
|
||||
mutex_unlock(&clock_list_lock);
|
||||
}
|
||||
|
||||
int clk_enable(struct clk *c)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (c->refcnt == 0) {
|
||||
if (c->parent) {
|
||||
ret = clk_enable(c->parent);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (c->ops && c->ops->enable) {
|
||||
ret = c->ops->enable(c);
|
||||
if (ret) {
|
||||
if (c->parent)
|
||||
clk_disable(c->parent);
|
||||
goto out;
|
||||
}
|
||||
c->state = ON;
|
||||
c->set = true;
|
||||
}
|
||||
}
|
||||
c->refcnt++;
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_enable);
|
||||
|
||||
void clk_disable(struct clk *c)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (c->refcnt == 0) {
|
||||
WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return;
|
||||
}
|
||||
if (c->refcnt == 1) {
|
||||
if (c->ops && c->ops->disable)
|
||||
c->ops->disable(c);
|
||||
|
||||
if (c->parent)
|
||||
clk_disable(c->parent);
|
||||
|
||||
c->state = OFF;
|
||||
}
|
||||
c->refcnt--;
|
||||
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_disable);
|
||||
|
||||
int clk_set_parent(struct clk *c, struct clk *parent)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
unsigned long new_rate;
|
||||
unsigned long old_rate;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (!c->ops || !c->ops->set_parent) {
|
||||
ret = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_rate = clk_predict_rate_from_parent(c, parent);
|
||||
old_rate = clk_get_rate_locked(c);
|
||||
|
||||
ret = c->ops->set_parent(c, parent);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_parent);
|
||||
|
||||
struct clk *clk_get_parent(struct clk *c)
|
||||
{
|
||||
return c->parent;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_parent);
|
||||
|
||||
int clk_set_rate_locked(struct clk *c, unsigned long rate)
|
||||
{
|
||||
long new_rate;
|
||||
|
||||
if (!c->ops || !c->ops->set_rate)
|
||||
return -ENOSYS;
|
||||
|
||||
if (rate > c->max_rate)
|
||||
rate = c->max_rate;
|
||||
|
||||
if (c->ops && c->ops->round_rate) {
|
||||
new_rate = c->ops->round_rate(c, rate);
|
||||
|
||||
if (new_rate < 0)
|
||||
return new_rate;
|
||||
|
||||
rate = new_rate;
|
||||
}
|
||||
|
||||
return c->ops->set_rate(c, rate);
|
||||
}
|
||||
|
||||
int clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
ret = clk_set_rate_locked(c, rate);
|
||||
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_rate);
|
||||
|
||||
|
||||
/* Must be called with clocks lock and all indvidual clock locks held */
|
||||
unsigned long clk_get_rate_all_locked(struct clk *c)
|
||||
{
|
||||
u64 rate;
|
||||
int mul = 1;
|
||||
int div = 1;
|
||||
struct clk *p = c;
|
||||
|
||||
while (p) {
|
||||
c = p;
|
||||
if (c->mul != 0 && c->div != 0) {
|
||||
mul *= c->mul;
|
||||
div *= c->div;
|
||||
}
|
||||
p = c->parent;
|
||||
}
|
||||
|
||||
rate = c->rate;
|
||||
rate *= mul;
|
||||
do_div(rate, div);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
long clk_round_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
unsigned long flags;
|
||||
long ret;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (!c->ops || !c->ops->round_rate) {
|
||||
ret = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rate > c->max_rate)
|
||||
rate = c->max_rate;
|
||||
|
||||
ret = c->ops->round_rate(c, rate);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_round_rate);
|
||||
|
||||
static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
|
||||
{
|
||||
struct clk *c;
|
||||
struct clk *p;
|
||||
struct clk *parent;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
c = tegra_get_clock_by_name(table->name);
|
||||
|
||||
if (!c) {
|
||||
pr_warning("Unable to initialize clock %s\n",
|
||||
pr_warn("Unable to initialize clock %s\n",
|
||||
table->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent = clk_get_parent(c);
|
||||
|
||||
if (table->parent) {
|
||||
p = tegra_get_clock_by_name(table->parent);
|
||||
if (!p) {
|
||||
pr_warning("Unable to find parent %s of clock %s\n",
|
||||
pr_warn("Unable to find parent %s of clock %s\n",
|
||||
table->parent, table->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (c->parent != p) {
|
||||
if (parent != p) {
|
||||
ret = clk_set_parent(c, p);
|
||||
if (ret) {
|
||||
pr_warning("Unable to set parent %s of clock %s: %d\n",
|
||||
pr_warn("Unable to set parent %s of clock %s: %d\n",
|
||||
table->parent, table->name, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -360,16 +109,16 @@ static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
|
||||
if (table->rate && table->rate != clk_get_rate(c)) {
|
||||
ret = clk_set_rate(c, table->rate);
|
||||
if (ret) {
|
||||
pr_warning("Unable to set clock %s to rate %lu: %d\n",
|
||||
pr_warn("Unable to set clock %s to rate %lu: %d\n",
|
||||
table->name, table->rate, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (table->enabled) {
|
||||
ret = clk_enable(c);
|
||||
ret = clk_prepare_enable(c);
|
||||
if (ret) {
|
||||
pr_warning("Unable to enable clock %s: %d\n",
|
||||
pr_warn("Unable to enable clock %s: %d\n",
|
||||
table->name, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -383,19 +132,20 @@ void tegra_clk_init_from_table(struct tegra_clk_init_table *table)
|
||||
for (; table->name; table++)
|
||||
tegra_clk_init_one_from_table(table);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_clk_init_from_table);
|
||||
|
||||
void tegra_periph_reset_deassert(struct clk *c)
|
||||
{
|
||||
BUG_ON(!c->ops->reset);
|
||||
c->ops->reset(c, false);
|
||||
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
|
||||
BUG_ON(!clk->reset);
|
||||
clk->reset(__clk_get_hw(c), false);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_periph_reset_deassert);
|
||||
|
||||
void tegra_periph_reset_assert(struct clk *c)
|
||||
{
|
||||
BUG_ON(!c->ops->reset);
|
||||
c->ops->reset(c, true);
|
||||
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
|
||||
BUG_ON(!clk->reset);
|
||||
clk->reset(__clk_get_hw(c), true);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_periph_reset_assert);
|
||||
|
||||
@ -405,268 +155,14 @@ EXPORT_SYMBOL(tegra_periph_reset_assert);
|
||||
int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (!c->ops || !c->ops->clk_cfg_ex) {
|
||||
if (!clk->clk_cfg_ex) {
|
||||
ret = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
ret = c->ops->clk_cfg_ex(c, p, setting);
|
||||
ret = clk->clk_cfg_ex(__clk_get_hw(c), p, setting);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static int __clk_lock_all_spinlocks(void)
|
||||
{
|
||||
struct clk *c;
|
||||
|
||||
list_for_each_entry(c, &clocks, node)
|
||||
if (!spin_trylock(&c->spinlock))
|
||||
goto unlock_spinlocks;
|
||||
|
||||
return 0;
|
||||
|
||||
unlock_spinlocks:
|
||||
list_for_each_entry_continue_reverse(c, &clocks, node)
|
||||
spin_unlock(&c->spinlock);
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static void __clk_unlock_all_spinlocks(void)
|
||||
{
|
||||
struct clk *c;
|
||||
|
||||
list_for_each_entry_reverse(c, &clocks, node)
|
||||
spin_unlock(&c->spinlock);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function retries until it can take all locks, and may take
|
||||
* an arbitrarily long time to complete.
|
||||
* Must be called with irqs enabled, returns with irqs disabled
|
||||
* Must be called with clock_list_lock held
|
||||
*/
|
||||
static void clk_lock_all(void)
|
||||
{
|
||||
int ret;
|
||||
retry:
|
||||
local_irq_disable();
|
||||
|
||||
ret = __clk_lock_all_spinlocks();
|
||||
if (ret)
|
||||
goto failed_spinlocks;
|
||||
|
||||
/* All locks taken successfully, return */
|
||||
return;
|
||||
|
||||
failed_spinlocks:
|
||||
local_irq_enable();
|
||||
yield();
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlocks all clocks after a clk_lock_all
|
||||
* Must be called with irqs disabled, returns with irqs enabled
|
||||
* Must be called with clock_list_lock held
|
||||
*/
|
||||
static void clk_unlock_all(void)
|
||||
{
|
||||
__clk_unlock_all_spinlocks();
|
||||
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static struct dentry *clk_debugfs_root;
|
||||
|
||||
|
||||
static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
|
||||
{
|
||||
struct clk *child;
|
||||
const char *state = "uninit";
|
||||
char div[8] = {0};
|
||||
|
||||
if (c->state == ON)
|
||||
state = "on";
|
||||
else if (c->state == OFF)
|
||||
state = "off";
|
||||
|
||||
if (c->mul != 0 && c->div != 0) {
|
||||
if (c->mul > c->div) {
|
||||
int mul = c->mul / c->div;
|
||||
int mul2 = (c->mul * 10 / c->div) % 10;
|
||||
int mul3 = (c->mul * 10) % c->div;
|
||||
if (mul2 == 0 && mul3 == 0)
|
||||
snprintf(div, sizeof(div), "x%d", mul);
|
||||
else if (mul3 == 0)
|
||||
snprintf(div, sizeof(div), "x%d.%d", mul, mul2);
|
||||
else
|
||||
snprintf(div, sizeof(div), "x%d.%d..", mul, mul2);
|
||||
} else {
|
||||
snprintf(div, sizeof(div), "%d%s", c->div / c->mul,
|
||||
(c->div % c->mul) ? ".5" : "");
|
||||
}
|
||||
}
|
||||
|
||||
seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n",
|
||||
level * 3 + 1, "",
|
||||
c->rate > c->max_rate ? '!' : ' ',
|
||||
!c->set ? '*' : ' ',
|
||||
30 - level * 3, c->name,
|
||||
state, c->refcnt, div, clk_get_rate_all_locked(c));
|
||||
|
||||
list_for_each_entry(child, &clocks, node) {
|
||||
if (child->parent != c)
|
||||
continue;
|
||||
|
||||
clock_tree_show_one(s, child, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int clock_tree_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct clk *c;
|
||||
seq_printf(s, " clock state ref div rate\n");
|
||||
seq_printf(s, "--------------------------------------------------------------\n");
|
||||
|
||||
mutex_lock(&clock_list_lock);
|
||||
|
||||
clk_lock_all();
|
||||
|
||||
list_for_each_entry(c, &clocks, node)
|
||||
if (c->parent == NULL)
|
||||
clock_tree_show_one(s, c, 0);
|
||||
|
||||
clk_unlock_all();
|
||||
|
||||
mutex_unlock(&clock_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clock_tree_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, clock_tree_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations clock_tree_fops = {
|
||||
.open = clock_tree_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int possible_parents_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct clk *c = s->private;
|
||||
int i;
|
||||
|
||||
for (i = 0; c->inputs[i].input; i++) {
|
||||
char *first = (i == 0) ? "" : " ";
|
||||
seq_printf(s, "%s%s", first, c->inputs[i].input->name);
|
||||
}
|
||||
seq_printf(s, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int possible_parents_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, possible_parents_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations possible_parents_fops = {
|
||||
.open = possible_parents_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int clk_debugfs_register_one(struct clk *c)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
d = debugfs_create_dir(c->name, clk_debugfs_root);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
c->dent = d;
|
||||
|
||||
d = debugfs_create_u8("refcnt", S_IRUGO, c->dent, (u8 *)&c->refcnt);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
if (c->inputs) {
|
||||
d = debugfs_create_file("possible_parents", S_IRUGO, c->dent,
|
||||
c, &possible_parents_fops);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
debugfs_remove_recursive(c->dent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int clk_debugfs_register(struct clk *c)
|
||||
{
|
||||
int err;
|
||||
struct clk *pa = c->parent;
|
||||
|
||||
if (pa && !pa->dent) {
|
||||
err = clk_debugfs_register(pa);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!c->dent) {
|
||||
err = clk_debugfs_register_one(c);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init tegra_clk_debugfs_init(void)
|
||||
{
|
||||
struct clk *c;
|
||||
struct dentry *d;
|
||||
int err = -ENOMEM;
|
||||
|
||||
d = debugfs_create_dir("clock", NULL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
clk_debugfs_root = d;
|
||||
|
||||
d = debugfs_create_file("clock_tree", S_IRUGO, clk_debugfs_root, NULL,
|
||||
&clock_tree_fops);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
list_for_each_entry(c, &clocks, node) {
|
||||
err = clk_debugfs_register(c);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
return 0;
|
||||
err_out:
|
||||
debugfs_remove_recursive(clk_debugfs_root);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -2,6 +2,7 @@
|
||||
* arch/arm/mach-tegra/include/mach/clock.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
@ -20,9 +21,9 @@
|
||||
#ifndef __MACH_TEGRA_CLOCK_H
|
||||
#define __MACH_TEGRA_CLOCK_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <mach/clk.h>
|
||||
|
||||
@ -52,7 +53,8 @@
|
||||
#define ENABLE_ON_INIT (1 << 28)
|
||||
#define PERIPH_ON_APB (1 << 29)
|
||||
|
||||
struct clk;
|
||||
struct clk_tegra;
|
||||
#define to_clk_tegra(_hw) container_of(_hw, struct clk_tegra, hw)
|
||||
|
||||
struct clk_mux_sel {
|
||||
struct clk *input;
|
||||
@ -68,47 +70,29 @@ struct clk_pll_freq_table {
|
||||
u8 cpcon;
|
||||
};
|
||||
|
||||
struct clk_ops {
|
||||
void (*init)(struct clk *);
|
||||
int (*enable)(struct clk *);
|
||||
void (*disable)(struct clk *);
|
||||
int (*set_parent)(struct clk *, struct clk *);
|
||||
int (*set_rate)(struct clk *, unsigned long);
|
||||
long (*round_rate)(struct clk *, unsigned long);
|
||||
void (*reset)(struct clk *, bool);
|
||||
int (*clk_cfg_ex)(struct clk *,
|
||||
enum tegra_clk_ex_param, u32);
|
||||
};
|
||||
|
||||
enum clk_state {
|
||||
UNINITIALIZED = 0,
|
||||
ON,
|
||||
OFF,
|
||||
};
|
||||
|
||||
struct clk {
|
||||
struct clk_tegra {
|
||||
/* node for master clocks list */
|
||||
struct list_head node; /* node for list of all clocks */
|
||||
struct list_head node; /* node for list of all clocks */
|
||||
struct clk_lookup lookup;
|
||||
struct clk_hw hw;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dent;
|
||||
#endif
|
||||
bool set;
|
||||
struct clk_ops *ops;
|
||||
unsigned long rate;
|
||||
unsigned long fixed_rate;
|
||||
unsigned long max_rate;
|
||||
unsigned long min_rate;
|
||||
u32 flags;
|
||||
const char *name;
|
||||
|
||||
u32 refcnt;
|
||||
enum clk_state state;
|
||||
struct clk *parent;
|
||||
u32 div;
|
||||
u32 mul;
|
||||
|
||||
const struct clk_mux_sel *inputs;
|
||||
u32 reg;
|
||||
u32 reg_shift;
|
||||
|
||||
@ -144,7 +128,8 @@ struct clk {
|
||||
} shared_bus_user;
|
||||
} u;
|
||||
|
||||
spinlock_t spinlock;
|
||||
void (*reset)(struct clk_hw *, bool);
|
||||
int (*clk_cfg_ex)(struct clk_hw *, enum tegra_clk_ex_param, u32);
|
||||
};
|
||||
|
||||
struct clk_duplicate {
|
||||
@ -159,13 +144,10 @@ struct tegra_clk_init_table {
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
void tegra_clk_add(struct clk *c);
|
||||
void tegra2_init_clocks(void);
|
||||
void tegra30_init_clocks(void);
|
||||
void clk_init(struct clk *clk);
|
||||
struct clk *tegra_get_clock_by_name(const char *name);
|
||||
int clk_reparent(struct clk *c, struct clk *parent);
|
||||
void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
|
||||
unsigned long clk_get_rate_locked(struct clk *c);
|
||||
int clk_set_rate_locked(struct clk *c, unsigned long rate);
|
||||
|
||||
#endif
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "fuse.h"
|
||||
#include "pmc.h"
|
||||
#include "apbio.h"
|
||||
#include "sleep.h"
|
||||
|
||||
/*
|
||||
* Storage for debug-macro.S's state.
|
||||
@ -135,6 +136,7 @@ void __init tegra20_init_early(void)
|
||||
tegra_init_cache(0x331, 0x441);
|
||||
tegra_pmc_init();
|
||||
tegra_powergate_init();
|
||||
tegra20_hotplug_init();
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
@ -147,11 +149,11 @@ void __init tegra30_init_early(void)
|
||||
tegra_init_cache(0x441, 0x551);
|
||||
tegra_pmc_init();
|
||||
tegra_powergate_init();
|
||||
tegra30_hotplug_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init tegra_init_late(void)
|
||||
{
|
||||
tegra_clk_debugfs_init();
|
||||
tegra_powergate_debugfs_init();
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ static struct cpufreq_frequency_table freq_table[] = {
|
||||
#define NUM_CPUS 2
|
||||
|
||||
static struct clk *cpu_clk;
|
||||
static struct clk *pll_x_clk;
|
||||
static struct clk *pll_p_clk;
|
||||
static struct clk *emc_clk;
|
||||
|
||||
static unsigned long target_cpu_speed[NUM_CPUS];
|
||||
@ -71,6 +73,42 @@ static unsigned int tegra_getspeed(unsigned int cpu)
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int tegra_cpu_clk_set_rate(unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Take an extra reference to the main pll so it doesn't turn
|
||||
* off when we move the cpu off of it
|
||||
*/
|
||||
clk_prepare_enable(pll_x_clk);
|
||||
|
||||
ret = clk_set_parent(cpu_clk, pll_p_clk);
|
||||
if (ret) {
|
||||
pr_err("Failed to switch cpu to clock pll_p\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rate == clk_get_rate(pll_p_clk))
|
||||
goto out;
|
||||
|
||||
ret = clk_set_rate(pll_x_clk, rate);
|
||||
if (ret) {
|
||||
pr_err("Failed to change pll_x to %lu\n", rate);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = clk_set_parent(cpu_clk, pll_x_clk);
|
||||
if (ret) {
|
||||
pr_err("Failed to switch cpu to clock pll_x\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
clk_disable_unprepare(pll_x_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_update_cpu_speed(unsigned long rate)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -101,7 +139,7 @@ static int tegra_update_cpu_speed(unsigned long rate)
|
||||
freqs.old, freqs.new);
|
||||
#endif
|
||||
|
||||
ret = clk_set_rate(cpu_clk, freqs.new * 1000);
|
||||
ret = tegra_cpu_clk_set_rate(freqs.new * 1000);
|
||||
if (ret) {
|
||||
pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n",
|
||||
freqs.new);
|
||||
@ -183,6 +221,14 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
|
||||
if (IS_ERR(cpu_clk))
|
||||
return PTR_ERR(cpu_clk);
|
||||
|
||||
pll_x_clk = clk_get_sys(NULL, "pll_x");
|
||||
if (IS_ERR(pll_x_clk))
|
||||
return PTR_ERR(pll_x_clk);
|
||||
|
||||
pll_p_clk = clk_get_sys(NULL, "pll_p");
|
||||
if (IS_ERR(pll_p_clk))
|
||||
return PTR_ERR(pll_p_clk);
|
||||
|
||||
emc_clk = clk_get_sys("cpu", "emc");
|
||||
if (IS_ERR(emc_clk)) {
|
||||
clk_put(cpu_clk);
|
||||
|
@ -7,17 +7,13 @@
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "reset.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define APB_MISC_GP_HIDREV 0x804
|
||||
#define PMC_SCRATCH41 0x140
|
||||
|
||||
#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
|
||||
|
||||
.macro mov32, reg, val
|
||||
movw \reg, #:lower16:\val
|
||||
movt \reg, #:upper16:\val
|
||||
.endm
|
||||
|
||||
.section ".text.head", "ax"
|
||||
__CPUINIT
|
||||
|
||||
|
@ -1,91 +1,23 @@
|
||||
/*
|
||||
* linux/arch/arm/mach-realview/hotplug.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* All Rights Reserved
|
||||
* Copyright (c) 2010, 2012 NVIDIA Corporation. 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/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
static inline void cpu_enter_lowpower(void)
|
||||
{
|
||||
unsigned int v;
|
||||
#include "sleep.h"
|
||||
#include "tegra_cpu_car.h"
|
||||
|
||||
flush_cache_all();
|
||||
asm volatile(
|
||||
" mcr p15, 0, %1, c7, c5, 0\n"
|
||||
" mcr p15, 0, %1, c7, c10, 4\n"
|
||||
/*
|
||||
* Turn off coherency
|
||||
*/
|
||||
" mrc p15, 0, %0, c1, c0, 1\n"
|
||||
" bic %0, %0, #0x20\n"
|
||||
" mcr p15, 0, %0, c1, c0, 1\n"
|
||||
" mrc p15, 0, %0, c1, c0, 0\n"
|
||||
" bic %0, %0, %2\n"
|
||||
" mcr p15, 0, %0, c1, c0, 0\n"
|
||||
: "=&r" (v)
|
||||
: "r" (0), "Ir" (CR_C)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
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, #0x20\n"
|
||||
" mcr p15, 0, %0, c1, c0, 1\n"
|
||||
: "=&r" (v)
|
||||
: "Ir" (CR_C)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
|
||||
{
|
||||
/*
|
||||
* there is no power-control hardware on this platform, so all
|
||||
* we can do is put the core into WFI; this is safe as the calling
|
||||
* code will have already disabled interrupts
|
||||
*/
|
||||
for (;;) {
|
||||
/*
|
||||
* here's the WFI
|
||||
*/
|
||||
asm(".word 0xe320f003\n"
|
||||
:
|
||||
:
|
||||
: "memory", "cc");
|
||||
|
||||
/*if (pen_release == cpu) {*/
|
||||
/*
|
||||
* OK, proper wakeup, we're done
|
||||
*/
|
||||
break;
|
||||
/*}*/
|
||||
|
||||
/*
|
||||
* Getting here, means that we have come out of WFI without
|
||||
* having been woken up - this shouldn't happen
|
||||
*
|
||||
* Just note it happening - when we're woken, we can report
|
||||
* its occurrence.
|
||||
*/
|
||||
(*spurious)++;
|
||||
}
|
||||
}
|
||||
static void (*tegra_hotplug_shutdown)(void);
|
||||
|
||||
int platform_cpu_kill(unsigned int cpu)
|
||||
{
|
||||
@ -99,22 +31,20 @@ int platform_cpu_kill(unsigned int cpu)
|
||||
*/
|
||||
void platform_cpu_die(unsigned int cpu)
|
||||
{
|
||||
int spurious = 0;
|
||||
cpu = cpu_logical_map(cpu);
|
||||
|
||||
/*
|
||||
* we're ready for shutdown now, so do it
|
||||
*/
|
||||
cpu_enter_lowpower();
|
||||
platform_do_lowpower(cpu, &spurious);
|
||||
/* Flush the L1 data cache. */
|
||||
flush_cache_all();
|
||||
|
||||
/*
|
||||
* bring this CPU back into the world of cache
|
||||
* coherency, and then restore interrupts
|
||||
*/
|
||||
cpu_leave_lowpower();
|
||||
/* Shut down the current CPU. */
|
||||
tegra_hotplug_shutdown();
|
||||
|
||||
if (spurious)
|
||||
pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
|
||||
/* Clock gate the CPU */
|
||||
tegra_wait_cpu_in_reset(cpu);
|
||||
tegra_disable_cpu_clock(cpu);
|
||||
|
||||
/* Should never return here. */
|
||||
BUG();
|
||||
}
|
||||
|
||||
int platform_cpu_disable(unsigned int cpu)
|
||||
@ -125,3 +55,19 @@ int platform_cpu_disable(unsigned int cpu)
|
||||
*/
|
||||
return cpu == 0 ? -EPERM : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
extern void tegra20_hotplug_shutdown(void);
|
||||
void __init tegra20_hotplug_init(void)
|
||||
{
|
||||
tegra_hotplug_shutdown = tegra20_hotplug_shutdown;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
extern void tegra30_hotplug_shutdown(void);
|
||||
void __init tegra30_hotplug_init(void)
|
||||
{
|
||||
tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
|
||||
}
|
||||
#endif
|
||||
|
@ -34,7 +34,10 @@ enum tegra_clk_ex_param {
|
||||
void tegra_periph_reset_deassert(struct clk *c);
|
||||
void tegra_periph_reset_assert(struct clk *c);
|
||||
|
||||
#ifndef CONFIG_COMMON_CLK
|
||||
unsigned long clk_get_rate_all_locked(struct clk *c);
|
||||
#endif
|
||||
|
||||
void tegra2_sdmmc_tap_delay(struct clk *c, int delay);
|
||||
int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting);
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "fuse.h"
|
||||
#include "flowctrl.h"
|
||||
#include "reset.h"
|
||||
#include "tegra_cpu_car.h"
|
||||
|
||||
extern void tegra_secondary_startup(void);
|
||||
|
||||
@ -38,17 +39,6 @@ static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
|
||||
|
||||
#define EVP_CPU_RESET_VECTOR \
|
||||
(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
|
||||
#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
|
||||
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
|
||||
#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
|
||||
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
|
||||
#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
|
||||
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
|
||||
#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
|
||||
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
|
||||
|
||||
#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
|
||||
#define CPU_RESET(cpu) (0x1111ul<<(cpu))
|
||||
|
||||
void __cpuinit platform_secondary_init(unsigned int cpu)
|
||||
{
|
||||
@ -63,13 +53,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
|
||||
|
||||
static int tegra20_power_up_cpu(unsigned int cpu)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Enable the CPU clock. */
|
||||
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
|
||||
writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
|
||||
barrier();
|
||||
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
|
||||
tegra_enable_cpu_clock(cpu);
|
||||
|
||||
/* Clear flow controller CSR. */
|
||||
flowctrl_write_cpu_csr(cpu, 0);
|
||||
@ -79,7 +64,6 @@ static int tegra20_power_up_cpu(unsigned int cpu)
|
||||
|
||||
static int tegra30_power_up_cpu(unsigned int cpu)
|
||||
{
|
||||
u32 reg;
|
||||
int ret, pwrgateid;
|
||||
unsigned long timeout;
|
||||
|
||||
@ -103,8 +87,7 @@ static int tegra30_power_up_cpu(unsigned int cpu)
|
||||
}
|
||||
|
||||
/* CPU partition is powered. Enable the CPU clock. */
|
||||
writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
|
||||
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
|
||||
tegra_enable_cpu_clock(cpu);
|
||||
udelay(10);
|
||||
|
||||
/* Remove I/O clamps. */
|
||||
@ -128,8 +111,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
* via the flow controller). This will have no effect on first boot
|
||||
* of the CPU since it should already be in reset.
|
||||
*/
|
||||
writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
|
||||
dmb();
|
||||
tegra_put_cpu_in_reset(cpu);
|
||||
|
||||
/*
|
||||
* Unhalt the CPU. If the flow controller was used to power-gate the
|
||||
@ -155,8 +137,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
goto done;
|
||||
|
||||
/* Take the CPU out of reset. */
|
||||
writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
|
||||
wmb();
|
||||
tegra_cpu_out_of_reset(cpu);
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
82
arch/arm/mach-tegra/sleep-t20.S
Normal file
82
arch/arm/mach-tegra/sleep-t20.S
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
|
||||
* Copyright (c) 2011, Google, Inc.
|
||||
*
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
* Gary King <gking@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
|
||||
#include <mach/iomap.h>
|
||||
|
||||
#include "sleep.h"
|
||||
#include "flowctrl.h"
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
|
||||
/*
|
||||
* tegra20_hotplug_shutdown(void)
|
||||
*
|
||||
* puts the current cpu in reset
|
||||
* should never return
|
||||
*/
|
||||
ENTRY(tegra20_hotplug_shutdown)
|
||||
/* Turn off SMP coherency */
|
||||
exit_smp r4, r5
|
||||
|
||||
/* Put this CPU down */
|
||||
cpu_id r0
|
||||
bl tegra20_cpu_shutdown
|
||||
mov pc, lr @ should never get here
|
||||
ENDPROC(tegra20_hotplug_shutdown)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_shutdown(int cpu)
|
||||
*
|
||||
* r0 is cpu to reset
|
||||
*
|
||||
* puts the specified CPU in wait-for-event mode on the flow controller
|
||||
* and puts the CPU in reset
|
||||
* can be called on the current cpu or another cpu
|
||||
* if called on the current cpu, does not return
|
||||
* MUST NOT BE CALLED FOR CPU 0.
|
||||
*
|
||||
* corrupts r0-r3, r12
|
||||
*/
|
||||
ENTRY(tegra20_cpu_shutdown)
|
||||
cmp r0, #0
|
||||
moveq pc, lr @ must not be called for CPU 0
|
||||
|
||||
cpu_to_halt_reg r1, r0
|
||||
ldr r3, =TEGRA_FLOW_CTRL_VIRT
|
||||
mov r2, #FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME
|
||||
str r2, [r3, r1] @ put flow controller in wait event mode
|
||||
ldr r2, [r3, r1]
|
||||
isb
|
||||
dsb
|
||||
movw r1, 0x1011
|
||||
mov r1, r1, lsl r0
|
||||
ldr r3, =TEGRA_CLK_RESET_VIRT
|
||||
str r1, [r3, #0x340] @ put slave CPU in reset
|
||||
isb
|
||||
dsb
|
||||
cpu_id r3
|
||||
cmp r3, r0
|
||||
beq .
|
||||
mov pc, lr
|
||||
ENDPROC(tegra20_cpu_shutdown)
|
||||
#endif
|
107
arch/arm/mach-tegra/sleep-t30.S
Normal file
107
arch/arm/mach-tegra/sleep-t30.S
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
|
||||
#include <mach/iomap.h>
|
||||
|
||||
#include "sleep.h"
|
||||
#include "flowctrl.h"
|
||||
|
||||
#define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
|
||||
/*
|
||||
* tegra30_hotplug_shutdown(void)
|
||||
*
|
||||
* Powergates the current CPU.
|
||||
* Should never return.
|
||||
*/
|
||||
ENTRY(tegra30_hotplug_shutdown)
|
||||
/* Turn off SMP coherency */
|
||||
exit_smp r4, r5
|
||||
|
||||
/* Powergate this CPU */
|
||||
mov r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
|
||||
bl tegra30_cpu_shutdown
|
||||
mov pc, lr @ should never get here
|
||||
ENDPROC(tegra30_hotplug_shutdown)
|
||||
|
||||
/*
|
||||
* tegra30_cpu_shutdown(unsigned long flags)
|
||||
*
|
||||
* Puts the current CPU in wait-for-event mode on the flow controller
|
||||
* and powergates it -- flags (in R0) indicate the request type.
|
||||
* Must never be called for CPU 0.
|
||||
*
|
||||
* corrupts r0-r4, r12
|
||||
*/
|
||||
ENTRY(tegra30_cpu_shutdown)
|
||||
cpu_id r3
|
||||
cmp r3, #0
|
||||
moveq pc, lr @ Must never be called for CPU 0
|
||||
|
||||
ldr r12, =TEGRA_FLOW_CTRL_VIRT
|
||||
cpu_to_csr_reg r1, r3
|
||||
add r1, r1, r12 @ virtual CSR address for this CPU
|
||||
cpu_to_halt_reg r2, r3
|
||||
add r2, r2, r12 @ virtual HALT_EVENTS address for this CPU
|
||||
|
||||
/*
|
||||
* Clear this CPU's "event" and "interrupt" flags and power gate
|
||||
* it when halting but not before it is in the "WFE" state.
|
||||
*/
|
||||
movw r12, \
|
||||
FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | \
|
||||
FLOW_CTRL_CSR_ENABLE
|
||||
mov r4, #(1 << 4)
|
||||
orr r12, r12, r4, lsl r3
|
||||
str r12, [r1]
|
||||
|
||||
/* Halt this CPU. */
|
||||
mov r3, #0x400
|
||||
delay_1:
|
||||
subs r3, r3, #1 @ delay as a part of wfe war.
|
||||
bge delay_1;
|
||||
cpsid a @ disable imprecise aborts.
|
||||
ldr r3, [r1] @ read CSR
|
||||
str r3, [r1] @ clear CSR
|
||||
tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
|
||||
movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug
|
||||
str r3, [r2]
|
||||
ldr r0, [r2]
|
||||
b wfe_war
|
||||
|
||||
__cpu_reset_again:
|
||||
dsb
|
||||
.align 5
|
||||
wfe @ CPU should be power gated here
|
||||
wfe_war:
|
||||
b __cpu_reset_again
|
||||
|
||||
/*
|
||||
* 38 nop's, which fills reset of wfe cache line and
|
||||
* 4 more cachelines with nop
|
||||
*/
|
||||
.rept 38
|
||||
nop
|
||||
.endr
|
||||
b . @ should never get here
|
||||
|
||||
ENDPROC(tegra30_cpu_shutdown)
|
||||
#endif
|
@ -29,36 +29,5 @@
|
||||
#include <mach/iomap.h>
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS \
|
||||
+ IO_PPSB_VIRT)
|
||||
|
||||
/* returns the offset of the flow controller halt register for a cpu */
|
||||
.macro cpu_to_halt_reg rd, rcpu
|
||||
cmp \rcpu, #0
|
||||
subne \rd, \rcpu, #1
|
||||
movne \rd, \rd, lsl #3
|
||||
addne \rd, \rd, #0x14
|
||||
moveq \rd, #0
|
||||
.endm
|
||||
|
||||
/* returns the offset of the flow controller csr register for a cpu */
|
||||
.macro cpu_to_csr_reg rd, rcpu
|
||||
cmp \rcpu, #0
|
||||
subne \rd, \rcpu, #1
|
||||
movne \rd, \rd, lsl #3
|
||||
addne \rd, \rd, #0x18
|
||||
moveq \rd, #8
|
||||
.endm
|
||||
|
||||
/* returns the ID of the current processor */
|
||||
.macro cpu_id, rd
|
||||
mrc p15, 0, \rd, c0, c0, 5
|
||||
and \rd, \rd, #0xF
|
||||
.endm
|
||||
|
||||
/* loads a 32-bit value into a register without a data access */
|
||||
.macro mov32, reg, val
|
||||
movw \reg, #:lower16:\val
|
||||
movt \reg, #:upper16:\val
|
||||
.endm
|
||||
|
85
arch/arm/mach-tegra/sleep.h
Normal file
85
arch/arm/mach-tegra/sleep.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_SLEEP_H
|
||||
#define __MACH_TEGRA_SLEEP_H
|
||||
|
||||
#include <mach/iomap.h>
|
||||
|
||||
#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \
|
||||
+ IO_CPU_VIRT)
|
||||
#define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS \
|
||||
+ IO_PPSB_VIRT)
|
||||
#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS \
|
||||
+ IO_PPSB_VIRT)
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
/* returns the offset of the flow controller halt register for a cpu */
|
||||
.macro cpu_to_halt_reg rd, rcpu
|
||||
cmp \rcpu, #0
|
||||
subne \rd, \rcpu, #1
|
||||
movne \rd, \rd, lsl #3
|
||||
addne \rd, \rd, #0x14
|
||||
moveq \rd, #0
|
||||
.endm
|
||||
|
||||
/* returns the offset of the flow controller csr register for a cpu */
|
||||
.macro cpu_to_csr_reg rd, rcpu
|
||||
cmp \rcpu, #0
|
||||
subne \rd, \rcpu, #1
|
||||
movne \rd, \rd, lsl #3
|
||||
addne \rd, \rd, #0x18
|
||||
moveq \rd, #8
|
||||
.endm
|
||||
|
||||
/* returns the ID of the current processor */
|
||||
.macro cpu_id, rd
|
||||
mrc p15, 0, \rd, c0, c0, 5
|
||||
and \rd, \rd, #0xF
|
||||
.endm
|
||||
|
||||
/* loads a 32-bit value into a register without a data access */
|
||||
.macro mov32, reg, val
|
||||
movw \reg, #:lower16:\val
|
||||
movt \reg, #:upper16:\val
|
||||
.endm
|
||||
|
||||
/* Macro to exit SMP coherency. */
|
||||
.macro exit_smp, tmp1, tmp2
|
||||
mrc p15, 0, \tmp1, c1, c0, 1 @ ACTLR
|
||||
bic \tmp1, \tmp1, #(1<<6) | (1<<0) @ clear ACTLR.SMP | ACTLR.FW
|
||||
mcr p15, 0, \tmp1, c1, c0, 1 @ ACTLR
|
||||
isb
|
||||
cpu_id \tmp1
|
||||
mov \tmp1, \tmp1, lsl #2
|
||||
mov \tmp2, #0xf
|
||||
mov \tmp2, \tmp2, lsl \tmp1
|
||||
mov32 \tmp1, TEGRA_ARM_PERIF_VIRT + 0xC
|
||||
str \tmp2, [\tmp1] @ invalidate SCU tags for CPU
|
||||
dsb
|
||||
.endm
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
void tegra20_hotplug_init(void);
|
||||
void tegra30_hotplug_init(void);
|
||||
#else
|
||||
static inline void tegra20_hotplug_init(void) {}
|
||||
static inline void tegra30_hotplug_init(void) {}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
1625
arch/arm/mach-tegra/tegra20_clocks.c
Normal file
1625
arch/arm/mach-tegra/tegra20_clocks.c
Normal file
File diff suppressed because it is too large
Load Diff
42
arch/arm/mach-tegra/tegra20_clocks.h
Normal file
42
arch/arm/mach-tegra/tegra20_clocks.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA20_CLOCK_H
|
||||
#define __MACH_TEGRA20_CLOCK_H
|
||||
|
||||
extern struct clk_ops tegra_clk_32k_ops;
|
||||
extern struct clk_ops tegra_pll_ops;
|
||||
extern struct clk_ops tegra_clk_m_ops;
|
||||
extern struct clk_ops tegra_pll_div_ops;
|
||||
extern struct clk_ops tegra_pllx_ops;
|
||||
extern struct clk_ops tegra_plle_ops;
|
||||
extern struct clk_ops tegra_clk_double_ops;
|
||||
extern struct clk_ops tegra_cdev_clk_ops;
|
||||
extern struct clk_ops tegra_audio_sync_clk_ops;
|
||||
extern struct clk_ops tegra_super_ops;
|
||||
extern struct clk_ops tegra_cpu_ops;
|
||||
extern struct clk_ops tegra_twd_ops;
|
||||
extern struct clk_ops tegra_cop_ops;
|
||||
extern struct clk_ops tegra_bus_ops;
|
||||
extern struct clk_ops tegra_blink_clk_ops;
|
||||
extern struct clk_ops tegra_emc_clk_ops;
|
||||
extern struct clk_ops tegra_periph_clk_ops;
|
||||
extern struct clk_ops tegra_clk_shared_bus_ops;
|
||||
|
||||
void tegra2_periph_clk_reset(struct clk_hw *hw, bool assert);
|
||||
void tegra2_cop_clk_reset(struct clk_hw *hw, bool assert);
|
||||
|
||||
#endif
|
1144
arch/arm/mach-tegra/tegra20_clocks_data.c
Normal file
1144
arch/arm/mach-tegra/tegra20_clocks_data.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
53
arch/arm/mach-tegra/tegra30_clocks.h
Normal file
53
arch/arm/mach-tegra/tegra30_clocks.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA30_CLOCK_H
|
||||
#define __MACH_TEGRA30_CLOCK_H
|
||||
|
||||
extern struct clk_ops tegra30_clk_32k_ops;
|
||||
extern struct clk_ops tegra30_clk_m_ops;
|
||||
extern struct clk_ops tegra_clk_m_div_ops;
|
||||
extern struct clk_ops tegra_pll_ref_ops;
|
||||
extern struct clk_ops tegra30_pll_ops;
|
||||
extern struct clk_ops tegra30_pll_div_ops;
|
||||
extern struct clk_ops tegra_plld_ops;
|
||||
extern struct clk_ops tegra30_plle_ops;
|
||||
extern struct clk_ops tegra_cml_clk_ops;
|
||||
extern struct clk_ops tegra_pciex_clk_ops;
|
||||
extern struct clk_ops tegra_sync_source_ops;
|
||||
extern struct clk_ops tegra30_audio_sync_clk_ops;
|
||||
extern struct clk_ops tegra30_clk_double_ops;
|
||||
extern struct clk_ops tegra_clk_out_ops;
|
||||
extern struct clk_ops tegra30_super_ops;
|
||||
extern struct clk_ops tegra30_blink_clk_ops;
|
||||
extern struct clk_ops tegra30_twd_ops;
|
||||
extern struct clk_ops tegra30_periph_clk_ops;
|
||||
extern struct clk_ops tegra30_dsib_clk_ops;
|
||||
extern struct clk_ops tegra_nand_clk_ops;
|
||||
extern struct clk_ops tegra_vi_clk_ops;
|
||||
extern struct clk_ops tegra_dtv_clk_ops;
|
||||
extern struct clk_ops tegra_clk_shared_bus_ops;
|
||||
|
||||
int tegra30_plld_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
void tegra30_periph_clk_reset(struct clk_hw *hw, bool assert);
|
||||
int tegra30_vi_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
int tegra30_nand_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
int tegra30_dtv_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
#endif
|
1372
arch/arm/mach-tegra/tegra30_clocks_data.c
Normal file
1372
arch/arm/mach-tegra/tegra30_clocks_data.c
Normal file
File diff suppressed because it is too large
Load Diff
87
arch/arm/mach-tegra/tegra_cpu_car.h
Normal file
87
arch/arm/mach-tegra/tegra_cpu_car.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_CPU_CAR_H
|
||||
#define __MACH_TEGRA_CPU_CAR_H
|
||||
|
||||
/*
|
||||
* Tegra CPU clock and reset control ops
|
||||
*
|
||||
* wait_for_reset:
|
||||
* keep waiting until the CPU in reset state
|
||||
* put_in_reset:
|
||||
* put the CPU in reset state
|
||||
* out_of_reset:
|
||||
* release the CPU from reset state
|
||||
* enable_clock:
|
||||
* CPU clock un-gate
|
||||
* disable_clock:
|
||||
* CPU clock gate
|
||||
*/
|
||||
struct tegra_cpu_car_ops {
|
||||
void (*wait_for_reset)(u32 cpu);
|
||||
void (*put_in_reset)(u32 cpu);
|
||||
void (*out_of_reset)(u32 cpu);
|
||||
void (*enable_clock)(u32 cpu);
|
||||
void (*disable_clock)(u32 cpu);
|
||||
};
|
||||
|
||||
extern struct tegra_cpu_car_ops *tegra_cpu_car_ops;
|
||||
|
||||
static inline void tegra_wait_cpu_in_reset(u32 cpu)
|
||||
{
|
||||
if (WARN_ON(!tegra_cpu_car_ops->wait_for_reset))
|
||||
return;
|
||||
|
||||
tegra_cpu_car_ops->wait_for_reset(cpu);
|
||||
}
|
||||
|
||||
static inline void tegra_put_cpu_in_reset(u32 cpu)
|
||||
{
|
||||
if (WARN_ON(!tegra_cpu_car_ops->put_in_reset))
|
||||
return;
|
||||
|
||||
tegra_cpu_car_ops->put_in_reset(cpu);
|
||||
}
|
||||
|
||||
static inline void tegra_cpu_out_of_reset(u32 cpu)
|
||||
{
|
||||
if (WARN_ON(!tegra_cpu_car_ops->out_of_reset))
|
||||
return;
|
||||
|
||||
tegra_cpu_car_ops->out_of_reset(cpu);
|
||||
}
|
||||
|
||||
static inline void tegra_enable_cpu_clock(u32 cpu)
|
||||
{
|
||||
if (WARN_ON(!tegra_cpu_car_ops->enable_clock))
|
||||
return;
|
||||
|
||||
tegra_cpu_car_ops->enable_clock(cpu);
|
||||
}
|
||||
|
||||
static inline void tegra_disable_cpu_clock(u32 cpu)
|
||||
{
|
||||
if (WARN_ON(!tegra_cpu_car_ops->disable_clock))
|
||||
return;
|
||||
|
||||
tegra_cpu_car_ops->disable_clock(cpu);
|
||||
}
|
||||
|
||||
void tegra20_cpu_car_ops_init(void);
|
||||
void tegra30_cpu_car_ops_init(void);
|
||||
|
||||
#endif /* __MACH_TEGRA_CPU_CAR_H */
|
@ -38,7 +38,7 @@ static int __init ux500_l2x0_init(void)
|
||||
{
|
||||
u32 aux_val = 0x3e000000;
|
||||
|
||||
if (cpu_is_u8500_family())
|
||||
if (cpu_is_u8500_family() || cpu_is_ux540_family())
|
||||
l2x0_base = __io_address(U8500_L2CC_BASE);
|
||||
else
|
||||
ux500_unknown_soc();
|
||||
|
@ -79,7 +79,7 @@ void __init u8500_map_io(void)
|
||||
|
||||
iotable_init(u8500_common_io_desc, ARRAY_SIZE(u8500_common_io_desc));
|
||||
|
||||
if (cpu_is_u9540())
|
||||
if (cpu_is_ux540_family())
|
||||
iotable_init(u9540_io_desc, ARRAY_SIZE(u9540_io_desc));
|
||||
else
|
||||
iotable_init(u8500_io_desc, ARRAY_SIZE(u8500_io_desc));
|
||||
|
@ -49,7 +49,7 @@ void __init ux500_init_irq(void)
|
||||
void __iomem *dist_base;
|
||||
void __iomem *cpu_base;
|
||||
|
||||
if (cpu_is_u8500_family()) {
|
||||
if (cpu_is_u8500_family() || cpu_is_ux540_family()) {
|
||||
dist_base = __io_address(U8500_GIC_DIST_BASE);
|
||||
cpu_base = __io_address(U8500_GIC_CPU_BASE);
|
||||
} else
|
||||
|
@ -41,43 +41,29 @@ static inline bool __attribute_const__ cpu_is_u8500(void)
|
||||
return dbx500_partnumber() == 0x8500;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8520(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x8520;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500_family(void)
|
||||
{
|
||||
return cpu_is_u8500() || cpu_is_u8520();
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u9540(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x9540;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500_family(void)
|
||||
static inline bool __attribute_const__ cpu_is_u8540(void)
|
||||
{
|
||||
return cpu_is_u8500() || cpu_is_u9540();
|
||||
return dbx500_partnumber() == 0x8540;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u5500(void)
|
||||
static inline bool cpu_is_ux540_family(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x5500;
|
||||
}
|
||||
|
||||
/*
|
||||
* 5500 revisions
|
||||
*/
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u5500v1(void)
|
||||
{
|
||||
return cpu_is_u5500() && (dbx500_revision() & 0xf0) == 0xA0;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u5500v2(void)
|
||||
{
|
||||
return (dbx500_id.revision & 0xf0) == 0xB0;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u5500v20(void)
|
||||
{
|
||||
return cpu_is_u5500() && ((dbx500_revision() & 0xf0) == 0xB0);
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u5500v21(void)
|
||||
{
|
||||
return cpu_is_u5500() && (dbx500_revision() == 0xB1);
|
||||
return cpu_is_u9540() || cpu_is_u8540();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -119,14 +105,14 @@ static inline bool cpu_is_u8500v21(void)
|
||||
return cpu_is_u8500() && (dbx500_revision() == 0xB1);
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500v22(void)
|
||||
{
|
||||
return cpu_is_u8500() && (dbx500_revision() == 0xB2);
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500v20_or_later(void)
|
||||
{
|
||||
/*
|
||||
* U9540 has so much in common with U8500 that is is considered a
|
||||
* U8500 variant.
|
||||
*/
|
||||
return cpu_is_u9540() ||
|
||||
(cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11());
|
||||
return (cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11());
|
||||
}
|
||||
|
||||
static inline bool ux500_is_svp(void)
|
||||
|
@ -48,7 +48,7 @@ static void write_pen_release(int val)
|
||||
|
||||
static void __iomem *scu_base_addr(void)
|
||||
{
|
||||
if (cpu_is_u8500_family())
|
||||
if (cpu_is_u8500_family() || cpu_is_ux540_family())
|
||||
return __io_address(U8500_SCU_BASE);
|
||||
else
|
||||
ux500_unknown_soc();
|
||||
@ -118,7 +118,7 @@ static void __init wakeup_secondary(void)
|
||||
{
|
||||
void __iomem *backupram;
|
||||
|
||||
if (cpu_is_u8500_family())
|
||||
if (cpu_is_u8500_family() || cpu_is_ux540_family())
|
||||
backupram = __io_address(U8500_BACKUPRAM0_BASE);
|
||||
else
|
||||
ux500_unknown_soc();
|
||||
|
@ -54,7 +54,7 @@ static void __init ux500_timer_init(void)
|
||||
void __iomem *tmp_base;
|
||||
struct device_node *np;
|
||||
|
||||
if (cpu_is_u8500_family()) {
|
||||
if (cpu_is_u8500_family() || cpu_is_ux540_family()) {
|
||||
mtu_timer_base = __io_address(U8500_MTU0_BASE);
|
||||
prcmu_timer_base = __io_address(U8500_PRCMU_TIMER_4_BASE);
|
||||
} else {
|
||||
|
@ -512,12 +512,16 @@ enum iomux_pins {
|
||||
#define MX31_PIN_CSPI3_SPI_RDY__CTS3 IOMUX_MODE(MX31_PIN_CSPI3_SPI_RDY, IOMUX_CONFIG_ALT1)
|
||||
#define MX31_PIN_CTS1__CTS1 IOMUX_MODE(MX31_PIN_CTS1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_RTS1__RTS1 IOMUX_MODE(MX31_PIN_RTS1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_RTS1__SFS IOMUX_MODE(MX31_PIN_RTS1, IOMUX_CONFIG_ALT2)
|
||||
#define MX31_PIN_TXD1__TXD1 IOMUX_MODE(MX31_PIN_TXD1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_TXD1__SCK IOMUX_MODE(MX31_PIN_TXD1, IOMUX_CONFIG_ALT2)
|
||||
#define MX31_PIN_RXD1__RXD1 IOMUX_MODE(MX31_PIN_RXD1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_RXD1__STXDA IOMUX_MODE(MX31_PIN_RXD1, IOMUX_CONFIG_ALT2)
|
||||
#define MX31_PIN_DCD_DCE1__DCD_DCE1 IOMUX_MODE(MX31_PIN_DCD_DCE1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_RI_DCE1__RI_DCE1 IOMUX_MODE(MX31_PIN_RI_DCE1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_DSR_DCE1__DSR_DCE1 IOMUX_MODE(MX31_PIN_DSR_DCE1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_DTR_DCE1__DTR_DCE1 IOMUX_MODE(MX31_PIN_DTR_DCE1, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_DTR_DCE1__SRXDA IOMUX_MODE(MX31_PIN_DTR_DCE1, IOMUX_CONFIG_ALT2)
|
||||
#define MX31_PIN_CTS2__CTS2 IOMUX_MODE(MX31_PIN_CTS2, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_RTS2__RTS2 IOMUX_MODE(MX31_PIN_RTS2, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_TXD2__TXD2 IOMUX_MODE(MX31_PIN_TXD2, IOMUX_CONFIG_FUNC)
|
||||
@ -721,6 +725,7 @@ enum iomux_pins {
|
||||
#define MX31_PIN_KEY_ROW2_KEY_ROW2 IOMUX_MODE(MX31_PIN_KEY_ROW2, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_KEY_ROW3_KEY_ROW3 IOMUX_MODE(MX31_PIN_KEY_ROW3, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_KEY_ROW4_KEY_ROW4 IOMUX_MODE(MX31_PIN_KEY_ROW4, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_KEY_ROW4_GPIO IOMUX_MODE(MX31_PIN_KEY_ROW4, IOMUX_CONFIG_GPIO)
|
||||
#define MX31_PIN_KEY_ROW5_KEY_ROW5 IOMUX_MODE(MX31_PIN_KEY_ROW5, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_KEY_ROW6_KEY_ROW6 IOMUX_MODE(MX31_PIN_KEY_ROW6, IOMUX_CONFIG_FUNC)
|
||||
#define MX31_PIN_KEY_ROW7_KEY_ROW7 IOMUX_MODE(MX31_PIN_KEY_ROW7, IOMUX_CONFIG_FUNC)
|
||||
|
@ -34,91 +34,98 @@
|
||||
.global imx_ssi_fiq_rx_buffer
|
||||
.global imx_ssi_fiq_tx_buffer
|
||||
|
||||
/*
|
||||
* imx_ssi_fiq_start is _intentionally_ not marked as a function symbol
|
||||
* using ENDPROC(). imx_ssi_fiq_start and imx_ssi_fiq_end are used to
|
||||
* mark the function body so that it can be copied to the FIQ vector in
|
||||
* the vectors page. imx_ssi_fiq_start should only be called as the result
|
||||
* of an FIQ: calling it directly will not work.
|
||||
*/
|
||||
imx_ssi_fiq_start:
|
||||
ldr r12, imx_ssi_fiq_base
|
||||
ldr r12, .L_imx_ssi_fiq_base
|
||||
|
||||
/* TX */
|
||||
ldr r11, imx_ssi_fiq_tx_buffer
|
||||
ldr r13, .L_imx_ssi_fiq_tx_buffer
|
||||
|
||||
/* shall we send? */
|
||||
ldr r13, [r12, #SSI_SIER]
|
||||
tst r13, #SSI_SIER_TFE0_EN
|
||||
ldr r11, [r12, #SSI_SIER]
|
||||
tst r11, #SSI_SIER_TFE0_EN
|
||||
beq 1f
|
||||
|
||||
/* TX FIFO empty? */
|
||||
ldr r13, [r12, #SSI_SISR]
|
||||
tst r13, #SSI_SISR_TFE0
|
||||
ldr r11, [r12, #SSI_SISR]
|
||||
tst r11, #SSI_SISR_TFE0
|
||||
beq 1f
|
||||
|
||||
mov r10, #0x10000
|
||||
sub r10, #1
|
||||
and r10, r10, r8 /* r10: current buffer offset */
|
||||
|
||||
add r11, r11, r10
|
||||
add r13, r13, r10
|
||||
|
||||
ldrh r13, [r11]
|
||||
strh r13, [r12, #SSI_STX0]
|
||||
ldrh r11, [r13]
|
||||
strh r11, [r12, #SSI_STX0]
|
||||
|
||||
ldrh r13, [r11, #2]
|
||||
strh r13, [r12, #SSI_STX0]
|
||||
ldrh r11, [r13, #2]
|
||||
strh r11, [r12, #SSI_STX0]
|
||||
|
||||
ldrh r13, [r11, #4]
|
||||
strh r13, [r12, #SSI_STX0]
|
||||
ldrh r11, [r13, #4]
|
||||
strh r11, [r12, #SSI_STX0]
|
||||
|
||||
ldrh r13, [r11, #6]
|
||||
strh r13, [r12, #SSI_STX0]
|
||||
ldrh r11, [r13, #6]
|
||||
strh r11, [r12, #SSI_STX0]
|
||||
|
||||
add r10, #8
|
||||
lsr r13, r8, #16 /* r13: buffer size */
|
||||
cmp r10, r13
|
||||
lslgt r8, r13, #16
|
||||
lsr r11, r8, #16 /* r11: buffer size */
|
||||
cmp r10, r11
|
||||
lslgt r8, r11, #16
|
||||
addle r8, #8
|
||||
1:
|
||||
/* RX */
|
||||
|
||||
/* shall we receive? */
|
||||
ldr r13, [r12, #SSI_SIER]
|
||||
tst r13, #SSI_SIER_RFF0_EN
|
||||
ldr r11, [r12, #SSI_SIER]
|
||||
tst r11, #SSI_SIER_RFF0_EN
|
||||
beq 1f
|
||||
|
||||
/* RX FIFO full? */
|
||||
ldr r13, [r12, #SSI_SISR]
|
||||
tst r13, #SSI_SISR_RFF0
|
||||
ldr r11, [r12, #SSI_SISR]
|
||||
tst r11, #SSI_SISR_RFF0
|
||||
beq 1f
|
||||
|
||||
ldr r11, imx_ssi_fiq_rx_buffer
|
||||
ldr r13, .L_imx_ssi_fiq_rx_buffer
|
||||
|
||||
mov r10, #0x10000
|
||||
sub r10, #1
|
||||
and r10, r10, r9 /* r10: current buffer offset */
|
||||
|
||||
add r11, r11, r10
|
||||
add r13, r13, r10
|
||||
|
||||
ldr r13, [r12, #SSI_SACNT]
|
||||
tst r13, #SSI_SACNT_AC97EN
|
||||
ldr r11, [r12, #SSI_SACNT]
|
||||
tst r11, #SSI_SACNT_AC97EN
|
||||
|
||||
ldr r13, [r12, #SSI_SRX0]
|
||||
strh r13, [r11]
|
||||
ldr r11, [r12, #SSI_SRX0]
|
||||
strh r11, [r13]
|
||||
|
||||
ldr r13, [r12, #SSI_SRX0]
|
||||
strh r13, [r11, #2]
|
||||
ldr r11, [r12, #SSI_SRX0]
|
||||
strh r11, [r13, #2]
|
||||
|
||||
/* dummy read to skip slot 12 */
|
||||
ldrne r13, [r12, #SSI_SRX0]
|
||||
ldrne r11, [r12, #SSI_SRX0]
|
||||
|
||||
ldr r13, [r12, #SSI_SRX0]
|
||||
strh r13, [r11, #4]
|
||||
ldr r11, [r12, #SSI_SRX0]
|
||||
strh r11, [r13, #4]
|
||||
|
||||
ldr r13, [r12, #SSI_SRX0]
|
||||
strh r13, [r11, #6]
|
||||
ldr r11, [r12, #SSI_SRX0]
|
||||
strh r11, [r13, #6]
|
||||
|
||||
/* dummy read to skip slot 12 */
|
||||
ldrne r13, [r12, #SSI_SRX0]
|
||||
ldrne r11, [r12, #SSI_SRX0]
|
||||
|
||||
add r10, #8
|
||||
lsr r13, r9, #16 /* r13: buffer size */
|
||||
cmp r10, r13
|
||||
lslgt r9, r13, #16
|
||||
lsr r11, r9, #16 /* r11: buffer size */
|
||||
cmp r10, r11
|
||||
lslgt r9, r11, #16
|
||||
addle r9, #8
|
||||
|
||||
1:
|
||||
@ -126,11 +133,15 @@ imx_ssi_fiq_start:
|
||||
subs pc, lr, #4
|
||||
|
||||
.align
|
||||
.L_imx_ssi_fiq_base:
|
||||
imx_ssi_fiq_base:
|
||||
.word 0x0
|
||||
.L_imx_ssi_fiq_rx_buffer:
|
||||
imx_ssi_fiq_rx_buffer:
|
||||
.word 0x0
|
||||
.L_imx_ssi_fiq_tx_buffer:
|
||||
imx_ssi_fiq_tx_buffer:
|
||||
.word 0x0
|
||||
.L_imx_ssi_fiq_end:
|
||||
imx_ssi_fiq_end:
|
||||
|
||||
|
@ -60,6 +60,7 @@ extern struct dev_pm_domain omap_device_pm_domain;
|
||||
* @_dev_wakeup_lat_limit: dev wakeup latency limit in nsec - set by OMAP PM
|
||||
* @_state: one of OMAP_DEVICE_STATE_* (see above)
|
||||
* @flags: device flags
|
||||
* @_driver_status: one of BUS_NOTIFY_*_DRIVER from <linux/device.h>
|
||||
*
|
||||
* Integrates omap_hwmod data into Linux platform_device.
|
||||
*
|
||||
@ -73,6 +74,7 @@ struct omap_device {
|
||||
struct omap_device_pm_latency *pm_lats;
|
||||
u32 dev_wakeup_lat;
|
||||
u32 _dev_wakeup_lat_limit;
|
||||
unsigned long _driver_status;
|
||||
u8 pm_lats_cnt;
|
||||
s8 pm_lat_level;
|
||||
u8 hwmods_cnt;
|
||||
|
@ -659,6 +659,7 @@ extern int omap2420_hwmod_init(void);
|
||||
extern int omap2430_hwmod_init(void);
|
||||
extern int omap3xxx_hwmod_init(void);
|
||||
extern int omap44xx_hwmod_init(void);
|
||||
extern int am33xx_hwmod_init(void);
|
||||
|
||||
extern int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois);
|
||||
|
||||
|
@ -388,17 +388,21 @@ static int _omap_device_notifier_call(struct notifier_block *nb,
|
||||
unsigned long event, void *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omap_device *od;
|
||||
|
||||
switch (event) {
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
if (pdev->dev.of_node)
|
||||
omap_device_build_from_dt(pdev);
|
||||
break;
|
||||
|
||||
case BUS_NOTIFY_DEL_DEVICE:
|
||||
if (pdev->archdata.od)
|
||||
omap_device_delete(pdev->archdata.od);
|
||||
break;
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
if (pdev->dev.of_node)
|
||||
omap_device_build_from_dt(pdev);
|
||||
/* fall through */
|
||||
default:
|
||||
od = to_omap_device(pdev);
|
||||
if (od)
|
||||
od->_driver_status = event;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
@ -802,6 +806,10 @@ static int _od_suspend_noirq(struct device *dev)
|
||||
struct omap_device *od = to_omap_device(pdev);
|
||||
int ret;
|
||||
|
||||
/* Don't attempt late suspend on a driver that is not bound */
|
||||
if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER)
|
||||
return 0;
|
||||
|
||||
ret = pm_generic_suspend_noirq(dev);
|
||||
|
||||
if (!ret && !pm_runtime_status_suspended(dev)) {
|
||||
@ -1175,3 +1183,41 @@ static int __init omap_device_init(void)
|
||||
return 0;
|
||||
}
|
||||
core_initcall(omap_device_init);
|
||||
|
||||
/**
|
||||
* omap_device_late_idle - idle devices without drivers
|
||||
* @dev: struct device * associated with omap_device
|
||||
* @data: unused
|
||||
*
|
||||
* Check the driver bound status of this device, and idle it
|
||||
* if there is no driver attached.
|
||||
*/
|
||||
static int __init omap_device_late_idle(struct device *dev, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omap_device *od = to_omap_device(pdev);
|
||||
|
||||
if (!od)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If omap_device state is enabled, but has no driver bound,
|
||||
* idle it.
|
||||
*/
|
||||
if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) {
|
||||
if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
|
||||
dev_warn(dev, "%s: enabled but no driver. Idling\n",
|
||||
__func__);
|
||||
omap_device_idle(pdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init omap_device_late_init(void)
|
||||
{
|
||||
bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(omap_device_late_init);
|
||||
|
@ -152,4 +152,6 @@ source "drivers/vme/Kconfig"
|
||||
|
||||
source "drivers/pwm/Kconfig"
|
||||
|
||||
source "drivers/irqchip/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -5,6 +5,8 @@
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
obj-y += irqchip/
|
||||
|
||||
# GPIO must come after pinctrl as gpios may need to mux pins etc
|
||||
obj-y += pinctrl/
|
||||
obj-y += gpio/
|
||||
|
@ -3,6 +3,7 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
|
||||
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
|
||||
clk-mux.o clk-divider.o clk-fixed-factor.o
|
||||
# SoCs specific
|
||||
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
|
||||
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
|
||||
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
|
||||
obj-$(CONFIG_ARCH_MXS) += mxs/
|
||||
|
59
drivers/clk/clk-bcm2835.c
Normal file
59
drivers/clk/clk-bcm2835.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
* Copyright (C) 2012 Stephen Warren
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/bcm2835.h>
|
||||
|
||||
/*
|
||||
* These are fixed clocks. They're probably not all root clocks and it may
|
||||
* be possible to turn them on and off but until this is mapped out better
|
||||
* it's the only way they can be used.
|
||||
*/
|
||||
void __init bcm2835_init_clocks(void)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, "sys_pclk", NULL, CLK_IS_ROOT,
|
||||
250000000);
|
||||
if (!clk)
|
||||
pr_err("sys_pclk not registered\n");
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT,
|
||||
126000000);
|
||||
if (!clk)
|
||||
pr_err("apb_pclk not registered\n");
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, CLK_IS_ROOT,
|
||||
3000000);
|
||||
if (!clk)
|
||||
pr_err("uart0_pclk not registered\n");
|
||||
ret = clk_register_clkdev(clk, NULL, "20201000.uart");
|
||||
if (ret)
|
||||
pr_err("uart0_pclk alias not registered\n");
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, CLK_IS_ROOT,
|
||||
125000000);
|
||||
if (!clk)
|
||||
pr_err("uart1_pclk not registered\n");
|
||||
ret = clk_register_clkdev(clk, NULL, "20215000.uart");
|
||||
if (ret)
|
||||
pr_err("uart0_pclk alias not registered\n");
|
||||
}
|
@ -13,3 +13,4 @@ obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
|
||||
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
|
||||
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
|
||||
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
|
||||
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
|
||||
|
161
drivers/clocksource/bcm2835_timer.c
Normal file
161
drivers/clocksource/bcm2835_timer.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2012 Simon Arlott
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/bcm2835_timer.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/sched_clock.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define REG_CONTROL 0x00
|
||||
#define REG_COUNTER_LO 0x04
|
||||
#define REG_COUNTER_HI 0x08
|
||||
#define REG_COMPARE(n) (0x0c + (n) * 4)
|
||||
#define MAX_TIMER 3
|
||||
#define DEFAULT_TIMER 3
|
||||
|
||||
struct bcm2835_timer {
|
||||
void __iomem *control;
|
||||
void __iomem *compare;
|
||||
int match_mask;
|
||||
struct clock_event_device evt;
|
||||
struct irqaction act;
|
||||
};
|
||||
|
||||
static void __iomem *system_clock __read_mostly;
|
||||
|
||||
static u32 notrace bcm2835_sched_read(void)
|
||||
{
|
||||
return readl_relaxed(system_clock);
|
||||
}
|
||||
|
||||
static void bcm2835_time_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt_dev)
|
||||
{
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
break;
|
||||
default:
|
||||
WARN(1, "%s: unhandled event mode %d\n", __func__, mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int bcm2835_time_set_next_event(unsigned long event,
|
||||
struct clock_event_device *evt_dev)
|
||||
{
|
||||
struct bcm2835_timer *timer = container_of(evt_dev,
|
||||
struct bcm2835_timer, evt);
|
||||
writel_relaxed(readl_relaxed(system_clock) + event,
|
||||
timer->compare);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct bcm2835_timer *timer = dev_id;
|
||||
void (*event_handler)(struct clock_event_device *);
|
||||
if (readl_relaxed(timer->control) & timer->match_mask) {
|
||||
writel_relaxed(timer->match_mask, timer->control);
|
||||
|
||||
event_handler = ACCESS_ONCE(timer->evt.event_handler);
|
||||
if (event_handler)
|
||||
event_handler(&timer->evt);
|
||||
return IRQ_HANDLED;
|
||||
} else {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static struct of_device_id bcm2835_time_match[] __initconst = {
|
||||
{ .compatible = "brcm,bcm2835-system-timer" },
|
||||
{}
|
||||
};
|
||||
|
||||
static void __init bcm2835_time_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
void __iomem *base;
|
||||
u32 freq;
|
||||
int irq;
|
||||
struct bcm2835_timer *timer;
|
||||
|
||||
node = of_find_matching_node(NULL, bcm2835_time_match);
|
||||
if (!node)
|
||||
panic("No bcm2835 timer node");
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base)
|
||||
panic("Can't remap registers");
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &freq))
|
||||
panic("Can't read clock-frequency");
|
||||
|
||||
system_clock = base + REG_COUNTER_LO;
|
||||
setup_sched_clock(bcm2835_sched_read, 32, freq);
|
||||
|
||||
clocksource_mmio_init(base + REG_COUNTER_LO, node->name,
|
||||
freq, 300, 32, clocksource_mmio_readl_up);
|
||||
|
||||
irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
|
||||
if (irq <= 0)
|
||||
panic("Can't parse IRQ");
|
||||
|
||||
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
|
||||
if (!timer)
|
||||
panic("Can't allocate timer struct\n");
|
||||
|
||||
timer->control = base + REG_CONTROL;
|
||||
timer->compare = base + REG_COMPARE(DEFAULT_TIMER);
|
||||
timer->match_mask = BIT(DEFAULT_TIMER);
|
||||
timer->evt.name = node->name;
|
||||
timer->evt.rating = 300;
|
||||
timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
|
||||
timer->evt.set_mode = bcm2835_time_set_mode;
|
||||
timer->evt.set_next_event = bcm2835_time_set_next_event;
|
||||
timer->evt.cpumask = cpumask_of(0);
|
||||
timer->act.name = node->name;
|
||||
timer->act.flags = IRQF_TIMER | IRQF_SHARED;
|
||||
timer->act.dev_id = timer;
|
||||
timer->act.handler = bcm2835_time_interrupt;
|
||||
|
||||
if (setup_irq(irq, &timer->act))
|
||||
panic("Can't set up timer IRQ\n");
|
||||
|
||||
clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
|
||||
|
||||
pr_info("bcm2835: system timer (irq = %d)\n", irq);
|
||||
}
|
||||
|
||||
struct sys_timer bcm2835_timer = {
|
||||
.init = bcm2835_time_init,
|
||||
};
|
0
drivers/irqchip/Kconfig
Normal file
0
drivers/irqchip/Kconfig
Normal file
1
drivers/irqchip/Makefile
Normal file
1
drivers/irqchip/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
|
223
drivers/irqchip/irq-bcm2835.c
Normal file
223
drivers/irqchip/irq-bcm2835.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2010 Broadcom
|
||||
* Copyright 2012 Simon Arlott, Chris Boot, Stephen Warren
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Quirk 1: Shortcut interrupts don't set the bank 1/2 register pending bits
|
||||
*
|
||||
* If an interrupt fires on bank 1 that isn't in the shortcuts list, bit 8
|
||||
* on bank 0 is set to signify that an interrupt in bank 1 has fired, and
|
||||
* to look in the bank 1 status register for more information.
|
||||
*
|
||||
* If an interrupt fires on bank 1 that _is_ in the shortcuts list, its
|
||||
* shortcut bit in bank 0 is set as well as its interrupt bit in the bank 1
|
||||
* status register, but bank 0 bit 8 is _not_ set.
|
||||
*
|
||||
* Quirk 2: You can't mask the register 1/2 pending interrupts
|
||||
*
|
||||
* In a proper cascaded interrupt controller, the interrupt lines with
|
||||
* cascaded interrupt controllers on them are just normal interrupt lines.
|
||||
* You can mask the interrupts and get on with things. With this controller
|
||||
* you can't do that.
|
||||
*
|
||||
* Quirk 3: The shortcut interrupts can't be (un)masked in bank 0
|
||||
*
|
||||
* Those interrupts that have shortcuts can only be masked/unmasked in
|
||||
* their respective banks' enable/disable registers. Doing so in the bank 0
|
||||
* enable/disable registers has no effect.
|
||||
*
|
||||
* The FIQ control register:
|
||||
* Bits 0-6: IRQ (index in order of interrupts from banks 1, 2, then 0)
|
||||
* Bit 7: Enable FIQ generation
|
||||
* Bits 8+: Unused
|
||||
*
|
||||
* An interrupt must be disabled before configuring it for FIQ generation
|
||||
* otherwise both handlers will fire at the same time!
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqchip/bcm2835.h>
|
||||
|
||||
#include <asm/exception.h>
|
||||
|
||||
/* Put the bank and irq (32 bits) into the hwirq */
|
||||
#define MAKE_HWIRQ(b, n) ((b << 5) | (n))
|
||||
#define HWIRQ_BANK(i) (i >> 5)
|
||||
#define HWIRQ_BIT(i) BIT(i & 0x1f)
|
||||
|
||||
#define NR_IRQS_BANK0 8
|
||||
#define BANK0_HWIRQ_MASK 0xff
|
||||
/* Shortcuts can't be disabled so any unknown new ones need to be masked */
|
||||
#define SHORTCUT1_MASK 0x00007c00
|
||||
#define SHORTCUT2_MASK 0x001f8000
|
||||
#define SHORTCUT_SHIFT 10
|
||||
#define BANK1_HWIRQ BIT(8)
|
||||
#define BANK2_HWIRQ BIT(9)
|
||||
#define BANK0_VALID_MASK (BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \
|
||||
| SHORTCUT1_MASK | SHORTCUT2_MASK)
|
||||
|
||||
#define REG_FIQ_CONTROL 0x0c
|
||||
|
||||
#define NR_BANKS 3
|
||||
#define IRQS_PER_BANK 32
|
||||
|
||||
static int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
|
||||
static int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
|
||||
static int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 };
|
||||
static int bank_irqs[] __initconst = { 8, 32, 32 };
|
||||
|
||||
static const int shortcuts[] = {
|
||||
7, 9, 10, 18, 19, /* Bank 1 */
|
||||
21, 22, 23, 24, 25, 30 /* Bank 2 */
|
||||
};
|
||||
|
||||
struct armctrl_ic {
|
||||
void __iomem *base;
|
||||
void __iomem *pending[NR_BANKS];
|
||||
void __iomem *enable[NR_BANKS];
|
||||
void __iomem *disable[NR_BANKS];
|
||||
struct irq_domain *domain;
|
||||
};
|
||||
|
||||
static struct armctrl_ic intc __read_mostly;
|
||||
|
||||
static void armctrl_mask_irq(struct irq_data *d)
|
||||
{
|
||||
writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]);
|
||||
}
|
||||
|
||||
static void armctrl_unmask_irq(struct irq_data *d)
|
||||
{
|
||||
writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]);
|
||||
}
|
||||
|
||||
static struct irq_chip armctrl_chip = {
|
||||
.name = "ARMCTRL-level",
|
||||
.irq_mask = armctrl_mask_irq,
|
||||
.irq_unmask = armctrl_unmask_irq
|
||||
};
|
||||
|
||||
static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
unsigned long *out_hwirq, unsigned int *out_type)
|
||||
{
|
||||
if (WARN_ON(intsize != 2))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(intspec[0] >= NR_BANKS))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(intspec[1] >= IRQS_PER_BANK))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0))
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = MAKE_HWIRQ(intspec[0], intspec[1]);
|
||||
*out_type = IRQ_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops armctrl_ops = {
|
||||
.xlate = armctrl_xlate
|
||||
};
|
||||
|
||||
static int __init armctrl_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
void __iomem *base;
|
||||
int irq, b, i;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base)
|
||||
panic("%s: unable to map IC registers\n",
|
||||
node->full_name);
|
||||
|
||||
intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0),
|
||||
&armctrl_ops, NULL);
|
||||
if (!intc.domain)
|
||||
panic("%s: unable to create IRQ domain\n", node->full_name);
|
||||
|
||||
for (b = 0; b < NR_BANKS; b++) {
|
||||
intc.pending[b] = base + reg_pending[b];
|
||||
intc.enable[b] = base + reg_enable[b];
|
||||
intc.disable[b] = base + reg_disable[b];
|
||||
|
||||
for (i = 0; i < bank_irqs[b]; i++) {
|
||||
irq = irq_create_mapping(intc.domain, MAKE_HWIRQ(b, i));
|
||||
BUG_ON(irq <= 0);
|
||||
irq_set_chip_and_handler(irq, &armctrl_chip,
|
||||
handle_level_irq);
|
||||
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id irq_of_match[] __initconst = {
|
||||
{ .compatible = "brcm,bcm2835-armctrl-ic", .data = armctrl_of_init }
|
||||
};
|
||||
|
||||
void __init bcm2835_init_irq(void)
|
||||
{
|
||||
of_irq_init(irq_of_match);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle each interrupt across the entire interrupt controller. This reads the
|
||||
* status register before handling each interrupt, which is necessary given that
|
||||
* handle_IRQ may briefly re-enable interrupts for soft IRQ handling.
|
||||
*/
|
||||
|
||||
static void armctrl_handle_bank(int bank, struct pt_regs *regs)
|
||||
{
|
||||
u32 stat, irq;
|
||||
|
||||
while ((stat = readl_relaxed(intc.pending[bank]))) {
|
||||
irq = MAKE_HWIRQ(bank, ffs(stat) - 1);
|
||||
handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
|
||||
}
|
||||
}
|
||||
|
||||
static void armctrl_handle_shortcut(int bank, struct pt_regs *regs,
|
||||
u32 stat)
|
||||
{
|
||||
u32 irq = MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]);
|
||||
handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
|
||||
}
|
||||
|
||||
asmlinkage void __exception_irq_entry bcm2835_handle_irq(
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
u32 stat, irq;
|
||||
|
||||
while ((stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK)) {
|
||||
if (stat & BANK0_HWIRQ_MASK) {
|
||||
irq = MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1);
|
||||
handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
|
||||
} else if (stat & SHORTCUT1_MASK) {
|
||||
armctrl_handle_shortcut(1, regs, stat & SHORTCUT1_MASK);
|
||||
} else if (stat & SHORTCUT2_MASK) {
|
||||
armctrl_handle_shortcut(2, regs, stat & SHORTCUT2_MASK);
|
||||
} else if (stat & BANK1_HWIRQ) {
|
||||
armctrl_handle_bank(1, regs);
|
||||
} else if (stat & BANK2_HWIRQ) {
|
||||
armctrl_handle_bank(2, regs);
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
}
|
22
include/linux/bcm2835_timer.h
Normal file
22
include/linux/bcm2835_timer.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2012 Simon Arlott
|
||||
*
|
||||
* 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 __BCM2835_TIMER_H
|
||||
#define __BCM2835_TIMER_H
|
||||
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
extern struct sys_timer bcm2835_timer;
|
||||
|
||||
#endif
|
24
include/linux/clk/bcm2835.h
Normal file
24
include/linux/clk/bcm2835.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_CLK_BCM2835_H_
|
||||
#define __LINUX_CLK_BCM2835_H_
|
||||
|
||||
void __init bcm2835_init_clocks(void);
|
||||
|
||||
#endif
|
29
include/linux/irqchip/bcm2835.h
Normal file
29
include/linux/irqchip/bcm2835.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Broadcom
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_IRQCHIP_BCM2835_H_
|
||||
#define __LINUX_IRQCHIP_BCM2835_H_
|
||||
|
||||
#include <asm/exception.h>
|
||||
|
||||
extern void bcm2835_init_irq(void);
|
||||
|
||||
extern asmlinkage void __exception_irq_entry bcm2835_handle_irq(
|
||||
struct pt_regs *regs);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user