Changes in 4.19.276 HID: asus: Remove check for same LED brightness on set HID: asus: use spinlock to protect concurrent accesses HID: asus: use spinlock to safely schedule workers ARM: OMAP2+: Fix memory leak in realtime_counter_init() ARM: zynq: Fix refcount leak in zynq_early_slcr_init arm64: dts: meson-gx: Fix Ethernet MAC address unit name arm64: dts: meson-gx: Fix the SCPI DVFS node name and unit address ARM: OMAP1: call platform_device_put() in error case in omap1_dm_timer_init() ARM: dts: exynos: correct wr-active property in Exynos3250 Rinato ARM: imx: Call ida_simple_remove() for ida_simple_get arm64: dts: amlogic: meson-gx: fix SCPI clock dvfs node name arm64: dts: meson-axg: enable SCPI arm64: dts: amlogic: meson-axg: fix SCPI clock dvfs node name arm64: dts: amlogic: meson-gx: add missing SCPI sensors compatible arm64: dts: amlogic: meson-gx: add missing unit address to rng node name arm64: dts: amlogic: meson-gxl: add missing unit address to eth-phy-mux node name arm64: dts: mediatek: mt7622: Add missing pwm-cells to pwm node blk-mq: remove stale comment for blk_mq_sched_mark_restart_hctx block: bio-integrity: Copy flags when bio_integrity_payload is cloned wifi: rsi: Fix memory leak in rsi_coex_attach() wifi: libertas: fix memory leak in lbs_init_adapter() wifi: rtl8xxxu: don't call dev_kfree_skb() under spin_lock_irqsave() rtlwifi: fix -Wpointer-sign warning wifi: rtlwifi: Fix global-out-of-bounds bug in _rtl8812ae_phy_set_txpower_limit() ipw2x00: switch from 'pci_' to 'dma_' API wifi: ipw2x00: don't call dev_kfree_skb() under spin_lock_irqsave() wifi: ipw2200: fix memory leak in ipw_wdev_init() wifi: brcmfmac: fix potential memory leak in brcmf_netdev_start_xmit() wifi: brcmfmac: unmap dma buffer in brcmf_msgbuf_alloc_pktid() wifi: libertas_tf: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas: if_usb: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas: main: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas: cmdresp: don't call kfree_skb() under spin_lock_irqsave() wifi: wl3501_cs: don't call kfree_skb() under spin_lock_irqsave() ACPICA: Drop port I/O validation for some regions genirq: Fix the return type of kstat_cpu_irqs_sum() lib/mpi: Fix buffer overrun when SG is too long ACPICA: nsrepair: handle cases without a return value correctly wifi: orinoco: check return value of hermes_write_wordrec() wifi: ath9k: htc_hst: free skb in ath9k_htc_rx_msg() if there is no callback function ath9k: hif_usb: simplify if-if to if-else ath9k: htc: clean up statistics macros wifi: ath9k: hif_usb: clean up skbs if ath9k_hif_usb_rx_stream() fails wifi: ath9k: Fix potential stack-out-of-bounds write in ath9k_wmi_rsp_callback() ACPI: battery: Fix missing NUL-termination with large strings crypto: seqiv - Handle EBUSY correctly powercap: fix possible name leak in powercap_register_zone() net/mlx5: Enhance debug print in page allocation failure irqchip/alpine-msi: Fix refcount leak in alpine_msix_init_domains irqchip/irq-mvebu-gicp: Fix refcount leak in mvebu_gicp_probe Bluetooth: L2CAP: Fix potential user-after-free libbpf: Fix alen calculation in libbpf_nla_dump_errormsg() rds: rds_rm_zerocopy_callback() correct order for list_add_tail() crypto: rsa-pkcs1pad - Use akcipher_request_complete m68k: /proc/hardware should depend on PROC_FS RISC-V: time: initialize hrtimer based broadcast clock event device wifi: iwl3945: Add missing check for create_singlethread_workqueue wifi: iwl4965: Add missing check for create_singlethread_workqueue() wifi: mwifiex: fix loop iterator in mwifiex_update_ampdu_txwinsize() crypto: crypto4xx - Call dma_unmap_page when done wifi: mac80211: make rate u32 in sta_set_rate_info_rx() can: esd_usb: Move mislocated storage of SJA1000_ECC_SEG bits in case of a bus error irqchip/irq-brcmstb-l2: Set IRQ_LEVEL for level triggered interrupts irqchip/irq-bcm7120-l2: Set IRQ_LEVEL for level triggered interrupts selftest: fib_tests: Always cleanup before exit drm: mxsfb: DRM_MXSFB should depend on ARCH_MXS || ARCH_MXC drm/bridge: megachips: Fix error handling in i2c_register_driver() drm: Clarify definition of the DRM_BUS_FLAG_(PIXDATA|SYNC)_* macros drm/vc4: dpi: Add option for inverting pixel clock and output enable drm/vc4: dpi: Fix format mapping for RGB565 gpu: ipu-v3: common: Add of_node_put() for reference returned by of_graph_get_port_by_id() drm/msm/hdmi: Add missing check for alloc_ordered_workqueue pinctrl: pinctrl-rockchip: Fix a bunch of kerneldoc misdemeanours pinctrl: rockchip: Fix refcount leak in rockchip_pinctrl_parse_groups ALSA: hda/ca0132: minor fix for allocation size drm/mipi-dsi: Fix byte order of 16-bit DCS set/get brightness drm/msm: use strscpy instead of strncpy drm/msm/dpu: Add check for pstates gpu: host1x: Don't skip assigning syncpoints to channels drm/mediatek: Drop unbalanced obj unref drm/mediatek: Clean dangling pointer on bind error path ASoC: soc-compress.c: fixup private_data on snd_soc_new_compress() gpio: vf610: connect GPIO label to dev name hwmon: (ltc2945) Handle error case in ltc2945_value_store scsi: aic94xx: Add missing check for dma_map_single() spi: bcm63xx-hsspi: fix pm_runtime spi: bcm63xx-hsspi: Fix multi-bit mode setting hwmon: (mlxreg-fan) Return zero speed for broken fan dm: remove flush_scheduled_work() during local_exit() nfsd: fix race to check ls_layouts cifs: Fix lost destroy smbd connection when MR allocate failed cifs: Fix warning and UAF when destroy the MR list gfs2: jdata writepage fix perf llvm: Fix inadvertent file creation perf tools: Fix auto-complete on aarch64 sparc: allow PM configs for sparc32 COMPILE_TEST selftests/ftrace: Fix bash specific "==" operator mfd: pcf50633-adc: Fix potential memleak in pcf50633_adc_async_read() mtd: rawnand: sunxi: Fix the size of the last OOB region Input: ads7846 - don't report pressure for ads7845 Input: ads7846 - don't check penirq immediately for 7845 powerpc/powernv/ioda: Skip unallocated resources when mapping to PE clk: Honor CLK_OPS_PARENT_ENABLE in clk_core_is_enabled() powerpc/pseries/lparcfg: add missing RTAS retry status handling powerpc/rtas: make all exports GPL powerpc/rtas: ensure 4KB alignment for rtas_data_buf MIPS: vpe-mt: drop physical_memsize media: platform: ti: Add missing check for devm_regulator_get powerpc: Remove linker flag from KBUILD_AFLAGS media: i2c: ov772x: Fix memleak in ov772x_probe() media: rc: Fix use-after-free bugs caused by ene_tx_irqsim() media: i2c: ov7670: 0 instead of -EINVAL was returned media: usb: siano: Fix use after free bugs caused by do_submit_urb rpmsg: glink: Avoid infinite loop on intent for missing channel udf: Define EFSCORRUPTED error code ARM: dts: exynos: Use Exynos5420 compatible for the MIPI video phy wifi: brcmfmac: Fix potential stack-out-of-bounds in brcmf_c_preinit_dcmds() rcu: Suppress smp_processor_id() complaint in synchronize_rcu_expedited_wait() thermal: intel: Fix unsigned comparison with less than zero timers: Prevent union confusion from unexpected restart_syscall() x86/bugs: Reset speculation control settings on init wifi: brcmfmac: ensure CLM version is null-terminated to prevent stack-out-of-bounds inet: fix fast path in __inet_hash_connect() ACPI: Don't build ACPICA with '-Os' net: bcmgenet: Add a check for oversized packets m68k: Check syscall_trace_enter() return code ACPI: video: Fix Lenovo Ideapad Z570 DMI match net/mlx5: fw_tracer: Fix debug print drm/amd/display: Fix potential null-deref in dm_resume drm/radeon: free iio for atombios when driver shutdown drm/msm/dsi: Add missing check for alloc_ordered_workqueue docs/scripts/gdb: add necessary make scripts_gdb step ASoC: kirkwood: Iterate over array indexes instead of using pointer math regulator: max77802: Bounds check regulator id against opmode regulator: s5m8767: Bounds check id indexing into arrays pinctrl: at91: use devm_kasprintf() to avoid potential leaks dm thin: add cond_resched() to various workqueue loops dm cache: add cond_resched() to various workqueue loops wifi: rtl8xxxu: fixing transmisison failure for rtl8192eu firmware: coreboot: framebuffer: Ignore reserved pixel color bits rtc: pm8xxx: fix set-alarm race s390: discard .interp section s390/kprobes: fix irq mask clobbering on kprobe reenter from post_handler s390/kprobes: fix current_kprobe never cleared after kprobes reenter ARM: dts: exynos: correct HDMI phy compatible in Exynos4 hfs: fix missing hfs_bnode_get() in __hfs_bnode_create fs: hfsplus: fix UAF issue in hfsplus_put_super f2fs: fix information leak in f2fs_move_inline_dirents() ocfs2: fix defrag path triggering jbd2 ASSERT ocfs2: fix non-auto defrag path not working issue udf: Truncate added extents on failed expansion udf: Do not bother merging very long extents udf: Do not update file length for failed writes to inline files udf: Fix file corruption when appending just after end of preallocated extent x86/virt: Force GIF=1 prior to disabling SVM (for reboot flows) x86/crash: Disable virt in core NMI crash handler to avoid double shootdown x86/reboot: Disable virtualization in an emergency if SVM is supported x86/reboot: Disable SVM, not just VMX, when stopping CPUs x86/kprobes: Fix __recover_optprobed_insn check optimizing logic x86/kprobes: Fix arch_check_optimized_kprobe check within optimized_kprobe range x86/microcode/amd: Remove load_microcode_amd()'s bsp parameter x86/microcode/AMD: Add a @cpu parameter to the reloading functions x86/microcode/AMD: Fix mixed steppings support x86/speculation: Allow enabling STIBP with legacy IBRS Documentation/hw-vuln: Document the interaction between IBRS and STIBP ima: Align ima_file_mmap() parameters with mmap_file LSM hook irqdomain: Fix association race irqdomain: Fix disassociation race irqdomain: Drop bogus fwspec-mapping error handling ALSA: ice1712: Do not left ice->gpio_mutex locked in aureon_add_controls() ext4: optimize ea_inode block expansion ext4: refuse to create ea block when umounted wifi: rtl8xxxu: Use a longer retry limit of 48 wifi: cfg80211: Fix use after free for wext dm flakey: fix logic when corrupting a bio dm flakey: don't corrupt the zero page ARM: dts: exynos: correct TMU phandle in Exynos4 ARM: dts: exynos: correct TMU phandle in Odroid XU rbd: avoid use-after-free in do_rbd_add() when rbd_dev_create() fails alpha: fix FEN fault handling mips: fix syscall_get_nr media: ipu3-cio2: Fix PM runtime usage_count in driver unbind ktest.pl: Give back console on Ctrt^C on monitor ktest.pl: Fix missing "end_monitor" when machine check fails ktest.pl: Add RUN_TIMEOUT option with default unlimited scsi: qla2xxx: Fix link failure in NPIV environment scsi: qla2xxx: Fix erroneous link down scsi: ses: Don't attach if enclosure has no components scsi: ses: Fix slab-out-of-bounds in ses_enclosure_data_process() scsi: ses: Fix possible addl_desc_ptr out-of-bounds accesses scsi: ses: Fix possible desc_ptr out-of-bounds accesses scsi: ses: Fix slab-out-of-bounds in ses_intf_remove() PCI: Avoid FLR for AMD FCH AHCI adapters drm/radeon: Fix eDP for single-display iMac11,2 wifi: ath9k: use proper statements in conditionals kbuild: Port silent mode detection to future gnu make. net/sched: Retire tcindex classifier fs/jfs: fix shift exponent db_agl2size negative pwm: stm32-lp: fix the check on arr and cmp registers update um: vector: Fix memory leak in vector_config ubi: ensure that VID header offset + VID header size <= alloc, size ubifs: Rectify space budget for ubifs_symlink() if symlink is encrypted ubifs: Rectify space budget for ubifs_xrename() ubifs: Fix wrong dirty space budget for dirty inode ubifs: do_rename: Fix wrong space budget when target inode's nlink > 1 ubifs: Reserve one leb for each journal head while doing budget ubi: Fix use-after-free when volume resizing failed ubi: Fix unreferenced object reported by kmemleak in ubi_resize_volume() ubi: Fix possible null-ptr-deref in ubi_free_volume() ubifs: Re-statistic cleaned znode count if commit failed ubifs: dirty_cow_znode: Fix memleak in error handling path ubifs: ubifs_writepage: Mark page dirty after writing inode failed ubi: Fix UAF wear-leveling entry in eraseblk_count_seq_show() ubi: ubi_wl_put_peb: Fix infinite loop when wear-leveling work failed x86: um: vdso: Add '%rcx' and '%r11' to the syscall clobber list watchdog: at91sam9_wdt: use devm_request_irq to avoid missing free_irq() in error path watchdog: Fix kmemleak in watchdog_cdev_register watchdog: pcwd_usb: Fix attempting to access uninitialized memory netfilter: ctnetlink: fix possible refcount leak in ctnetlink_create_conntrack() net: fix __dev_kfree_skb_any() vs drop monitor 9p/xen: fix version parsing 9p/xen: fix connection sequence 9p/rdma: unmap receive dma buffer in rdma_request()/post_recv() nfc: fix memory leak of se_io context in nfc_genl_se_io ARM: dts: spear320-hmi: correct STMPE GPIO compatible tcp: tcp_check_req() can be called from process context vc_screen: modify vcs_size() handling in vcs_read() scsi: ipr: Work around fortify-string warning thermal: intel: quark_dts: fix error pointer dereference tracing: Add NULL checks for buffer in ring_buffer_free_read_page() firmware/efi sysfb_efi: Add quirk for Lenovo IdeaPad Duet 3 media: uvcvideo: Handle cameras with invalid descriptors media: uvcvideo: Handle errors from calls to usb_string media: uvcvideo: Silence memcpy() run-time false positive warnings tty: fix out-of-bounds access in tty_driver_lookup_tty() tty: serial: fsl_lpuart: disable the CTS when send break signal mei: bus-fixup:upon error print return values of send and receive tools/iio/iio_utils:fix memory leak iio: accel: mma9551_core: Prevent uninitialized variable in mma9551_read_status_word() iio: accel: mma9551_core: Prevent uninitialized variable in mma9551_read_config_word() usb: host: xhci: mvebu: Iterate over array indexes instead of using pointer math USB: ene_usb6250: Allocate enough memory for full object usb: uvc: Enumerate valid values for color matching phy: rockchip-typec: Fix unsigned comparison with less than zero Bluetooth: hci_sock: purge socket queues in the destruct() callback s390/maccess: add no DAT mode to kernel_write s390/setup: init jump labels before command line parsing tcp: Fix listen() regression in 4.19.270 media: uvcvideo: Provide sync and async uvc_ctrl_status_event media: uvcvideo: Fix race condition with usb_kill_urb f2fs: fix cgroup writeback accounting with fs-layer encryption thermal: intel: powerclamp: Fix cur_state for multi package system Linux 4.19.276 Change-Id: Iaca469bb6e616eafeac4e834dad816acc2fe0f34 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
769 lines
20 KiB
C
769 lines
20 KiB
C
/*
|
|
* linux/arch/arm/mach-omap2/timer.c
|
|
*
|
|
* OMAP2 GP timer support.
|
|
*
|
|
* Copyright (C) 2009 Nokia Corporation
|
|
*
|
|
* Update to use new clocksource/clockevent layers
|
|
* Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
|
|
* Copyright (C) 2007 MontaVista Software, Inc.
|
|
*
|
|
* Original driver:
|
|
* Copyright (C) 2005 Nokia Corporation
|
|
* Author: Paul Mundt <paul.mundt@nokia.com>
|
|
* Juha Yrjölä <juha.yrjola@nokia.com>
|
|
* OMAP Dual-mode timer framework support by Timo Teras
|
|
*
|
|
* Some parts based off of TI's 24xx code:
|
|
*
|
|
* Copyright (C) 2004-2009 Texas Instruments, Inc.
|
|
*
|
|
* Roughly modelled after the OMAP1 MPU timer code.
|
|
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/time.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/err.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/clocksource.h>
|
|
#include <linux/clockchips.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/platform_data/dmtimer-omap.h>
|
|
#include <linux/sched_clock.h>
|
|
|
|
#include <asm/mach/time.h>
|
|
#include <asm/smp_twd.h>
|
|
|
|
#include "omap_hwmod.h"
|
|
#include "omap_device.h"
|
|
#include <plat/counter-32k.h>
|
|
#include <clocksource/timer-ti-dm.h>
|
|
|
|
#include "soc.h"
|
|
#include "common.h"
|
|
#include "control.h"
|
|
#include "powerdomain.h"
|
|
#include "omap-secure.h"
|
|
|
|
#define REALTIME_COUNTER_BASE 0x48243200
|
|
#define INCREMENTER_NUMERATOR_OFFSET 0x10
|
|
#define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14
|
|
#define NUMERATOR_DENUMERATOR_MASK 0xfffff000
|
|
|
|
/* Clockevent code */
|
|
|
|
/* Clockevent hwmod for am335x and am437x suspend */
|
|
static struct omap_hwmod *clockevent_gpt_hwmod;
|
|
|
|
/* Clockesource hwmod for am437x suspend */
|
|
static struct omap_hwmod *clocksource_gpt_hwmod;
|
|
|
|
struct dmtimer_clockevent {
|
|
struct clock_event_device dev;
|
|
struct omap_dm_timer timer;
|
|
};
|
|
|
|
static struct dmtimer_clockevent clockevent;
|
|
|
|
static struct omap_dm_timer *to_dmtimer(struct clock_event_device *clockevent)
|
|
{
|
|
struct dmtimer_clockevent *clkevt =
|
|
container_of(clockevent, struct dmtimer_clockevent, dev);
|
|
struct omap_dm_timer *timer = &clkevt->timer;
|
|
|
|
return timer;
|
|
}
|
|
|
|
#ifdef CONFIG_SOC_HAS_REALTIME_COUNTER
|
|
static unsigned long arch_timer_freq;
|
|
|
|
void set_cntfreq(void)
|
|
{
|
|
omap_smc1(OMAP5_DRA7_MON_SET_CNTFRQ_INDEX, arch_timer_freq);
|
|
}
|
|
#endif
|
|
|
|
static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct dmtimer_clockevent *clkevt = dev_id;
|
|
struct clock_event_device *evt = &clkevt->dev;
|
|
struct omap_dm_timer *timer = &clkevt->timer;
|
|
|
|
__omap_dm_timer_write_status(timer, OMAP_TIMER_INT_OVERFLOW);
|
|
evt->event_handler(evt);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int omap2_gp_timer_set_next_event(unsigned long cycles,
|
|
struct clock_event_device *evt)
|
|
{
|
|
struct omap_dm_timer *timer = to_dmtimer(evt);
|
|
|
|
__omap_dm_timer_load_start(timer, OMAP_TIMER_CTRL_ST,
|
|
0xffffffff - cycles, OMAP_TIMER_POSTED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int omap2_gp_timer_shutdown(struct clock_event_device *evt)
|
|
{
|
|
struct omap_dm_timer *timer = to_dmtimer(evt);
|
|
|
|
__omap_dm_timer_stop(timer, OMAP_TIMER_POSTED, timer->rate);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int omap2_gp_timer_set_periodic(struct clock_event_device *evt)
|
|
{
|
|
struct omap_dm_timer *timer = to_dmtimer(evt);
|
|
u32 period;
|
|
|
|
__omap_dm_timer_stop(timer, OMAP_TIMER_POSTED, timer->rate);
|
|
|
|
period = timer->rate / HZ;
|
|
period -= 1;
|
|
/* Looks like we need to first set the load value separately */
|
|
__omap_dm_timer_write(timer, OMAP_TIMER_LOAD_REG, 0xffffffff - period,
|
|
OMAP_TIMER_POSTED);
|
|
__omap_dm_timer_load_start(timer,
|
|
OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST,
|
|
0xffffffff - period, OMAP_TIMER_POSTED);
|
|
return 0;
|
|
}
|
|
|
|
static void omap_clkevt_idle(struct clock_event_device *unused)
|
|
{
|
|
if (!clockevent_gpt_hwmod)
|
|
return;
|
|
|
|
omap_hwmod_idle(clockevent_gpt_hwmod);
|
|
}
|
|
|
|
static void omap_clkevt_unidle(struct clock_event_device *evt)
|
|
{
|
|
struct omap_dm_timer *timer = to_dmtimer(evt);
|
|
|
|
if (!clockevent_gpt_hwmod)
|
|
return;
|
|
|
|
omap_hwmod_enable(clockevent_gpt_hwmod);
|
|
__omap_dm_timer_int_enable(timer, OMAP_TIMER_INT_OVERFLOW);
|
|
}
|
|
|
|
static const struct of_device_id omap_timer_match[] __initconst = {
|
|
{ .compatible = "ti,omap2420-timer", },
|
|
{ .compatible = "ti,omap3430-timer", },
|
|
{ .compatible = "ti,omap4430-timer", },
|
|
{ .compatible = "ti,omap5430-timer", },
|
|
{ .compatible = "ti,dm814-timer", },
|
|
{ .compatible = "ti,dm816-timer", },
|
|
{ .compatible = "ti,am335x-timer", },
|
|
{ .compatible = "ti,am335x-timer-1ms", },
|
|
{ }
|
|
};
|
|
|
|
static int omap_timer_add_disabled_property(struct device_node *np)
|
|
{
|
|
struct property *prop;
|
|
|
|
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
|
|
if (!prop)
|
|
return -ENOMEM;
|
|
|
|
prop->name = "status";
|
|
prop->value = "disabled";
|
|
prop->length = strlen(prop->value);
|
|
|
|
return of_add_property(np, prop);
|
|
}
|
|
|
|
static int omap_timer_update_dt(struct device_node *np)
|
|
{
|
|
int error = 0;
|
|
|
|
if (!of_device_is_compatible(np, "ti,omap-counter32k")) {
|
|
error = omap_timer_add_disabled_property(np);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
/* No parent interconnect target module configured? */
|
|
if (of_get_property(np, "ti,hwmods", NULL))
|
|
return error;
|
|
|
|
/* Tag parent interconnect target module disabled */
|
|
error = omap_timer_add_disabled_property(np->parent);
|
|
if (error)
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* omap_get_timer_dt - get a timer using device-tree
|
|
* @match - device-tree match structure for matching a device type
|
|
* @property - optional timer property to match
|
|
*
|
|
* Helper function to get a timer during early boot using device-tree for use
|
|
* as kernel system timer. Optionally, the property argument can be used to
|
|
* select a timer with a specific property. Once a timer is found then mark
|
|
* the timer node in device-tree as disabled, to prevent the kernel from
|
|
* registering this timer as a platform device and so no one else can use it.
|
|
*/
|
|
static struct device_node * __init omap_get_timer_dt(const struct of_device_id *match,
|
|
const char *property)
|
|
{
|
|
struct device_node *np;
|
|
int error;
|
|
|
|
for_each_matching_node(np, match) {
|
|
if (!of_device_is_available(np))
|
|
continue;
|
|
|
|
if (property && !of_get_property(np, property, NULL))
|
|
continue;
|
|
|
|
if (!property && (of_get_property(np, "ti,timer-alwon", NULL) ||
|
|
of_get_property(np, "ti,timer-dsp", NULL) ||
|
|
of_get_property(np, "ti,timer-pwm", NULL) ||
|
|
of_get_property(np, "ti,timer-secure", NULL)))
|
|
continue;
|
|
|
|
error = omap_timer_update_dt(np);
|
|
WARN(error, "%s: Could not update dt: %i\n", __func__, error);
|
|
|
|
return np;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* omap_dmtimer_init - initialisation function when device tree is used
|
|
*
|
|
* For secure OMAP3/DRA7xx devices, timers with device type "timer-secure"
|
|
* cannot be used by the kernel as they are reserved. Therefore, to prevent the
|
|
* kernel registering these devices remove them dynamically from the device
|
|
* tree on boot.
|
|
*/
|
|
static void __init omap_dmtimer_init(void)
|
|
{
|
|
struct device_node *np;
|
|
|
|
if (!cpu_is_omap34xx() && !soc_is_dra7xx())
|
|
return;
|
|
|
|
/* If we are a secure device, remove any secure timer nodes */
|
|
if ((omap_type() != OMAP2_DEVICE_TYPE_GP)) {
|
|
np = omap_get_timer_dt(omap_timer_match, "ti,timer-secure");
|
|
of_node_put(np);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* omap_dm_timer_get_errata - get errata flags for a timer
|
|
*
|
|
* Get the timer errata flags that are specific to the OMAP device being used.
|
|
*/
|
|
static u32 __init omap_dm_timer_get_errata(void)
|
|
{
|
|
if (cpu_is_omap24xx())
|
|
return 0;
|
|
|
|
return OMAP_TIMER_ERRATA_I103_I767;
|
|
}
|
|
|
|
static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
|
|
const char *fck_source,
|
|
const char *property,
|
|
const char **timer_name,
|
|
int posted)
|
|
{
|
|
const char *oh_name = NULL;
|
|
struct device_node *np;
|
|
struct omap_hwmod *oh;
|
|
struct clk *src;
|
|
int r = 0;
|
|
|
|
np = omap_get_timer_dt(omap_timer_match, property);
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
of_property_read_string_index(np, "ti,hwmods", 0, &oh_name);
|
|
if (!oh_name) {
|
|
of_property_read_string_index(np->parent, "ti,hwmods", 0,
|
|
&oh_name);
|
|
if (!oh_name)
|
|
return -ENODEV;
|
|
}
|
|
|
|
timer->irq = irq_of_parse_and_map(np, 0);
|
|
if (!timer->irq)
|
|
return -ENXIO;
|
|
|
|
timer->io_base = of_iomap(np, 0);
|
|
|
|
timer->fclk = of_clk_get_by_name(np, "fck");
|
|
|
|
of_node_put(np);
|
|
|
|
oh = omap_hwmod_lookup(oh_name);
|
|
if (!oh)
|
|
return -ENODEV;
|
|
|
|
*timer_name = oh->name;
|
|
|
|
if (!timer->io_base)
|
|
return -ENXIO;
|
|
|
|
omap_hwmod_setup_one(oh_name);
|
|
|
|
/* After the dmtimer is using hwmod these clocks won't be needed */
|
|
if (IS_ERR_OR_NULL(timer->fclk))
|
|
timer->fclk = clk_get(NULL, omap_hwmod_get_main_clk(oh));
|
|
if (IS_ERR(timer->fclk))
|
|
return PTR_ERR(timer->fclk);
|
|
|
|
src = clk_get(NULL, fck_source);
|
|
if (IS_ERR(src))
|
|
return PTR_ERR(src);
|
|
|
|
WARN(clk_set_parent(timer->fclk, src) < 0,
|
|
"Cannot set timer parent clock, no PLL clock driver?");
|
|
|
|
clk_put(src);
|
|
|
|
omap_hwmod_enable(oh);
|
|
__omap_dm_timer_init_regs(timer);
|
|
|
|
if (posted)
|
|
__omap_dm_timer_enable_posted(timer);
|
|
|
|
/* Check that the intended posted configuration matches the actual */
|
|
if (posted != timer->posted)
|
|
return -EINVAL;
|
|
|
|
timer->rate = clk_get_rate(timer->fclk);
|
|
timer->reserved = 1;
|
|
|
|
return r;
|
|
}
|
|
|
|
#if !defined(CONFIG_SMP) && defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
|
|
void tick_broadcast(const struct cpumask *mask)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void __init dmtimer_clkevt_init_common(struct dmtimer_clockevent *clkevt,
|
|
int gptimer_id,
|
|
const char *fck_source,
|
|
unsigned int features,
|
|
const struct cpumask *cpumask,
|
|
const char *property,
|
|
int rating, const char *name)
|
|
{
|
|
struct omap_dm_timer *timer = &clkevt->timer;
|
|
int res;
|
|
|
|
timer->id = gptimer_id;
|
|
timer->errata = omap_dm_timer_get_errata();
|
|
clkevt->dev.features = features;
|
|
clkevt->dev.rating = rating;
|
|
clkevt->dev.set_next_event = omap2_gp_timer_set_next_event;
|
|
clkevt->dev.set_state_shutdown = omap2_gp_timer_shutdown;
|
|
clkevt->dev.set_state_periodic = omap2_gp_timer_set_periodic;
|
|
clkevt->dev.set_state_oneshot = omap2_gp_timer_shutdown;
|
|
clkevt->dev.tick_resume = omap2_gp_timer_shutdown;
|
|
|
|
/*
|
|
* For clock-event timers we never read the timer counter and
|
|
* so we are not impacted by errata i103 and i767. Therefore,
|
|
* we can safely ignore this errata for clock-event timers.
|
|
*/
|
|
__omap_dm_timer_override_errata(timer, OMAP_TIMER_ERRATA_I103_I767);
|
|
|
|
res = omap_dm_timer_init_one(timer, fck_source, property,
|
|
&clkevt->dev.name, OMAP_TIMER_POSTED);
|
|
BUG_ON(res);
|
|
|
|
clkevt->dev.cpumask = cpumask;
|
|
clkevt->dev.irq = omap_dm_timer_get_irq(timer);
|
|
|
|
if (request_irq(clkevt->dev.irq, omap2_gp_timer_interrupt,
|
|
IRQF_TIMER | IRQF_IRQPOLL, name, clkevt))
|
|
pr_err("Failed to request irq %d (gp_timer)\n", clkevt->dev.irq);
|
|
|
|
__omap_dm_timer_int_enable(timer, OMAP_TIMER_INT_OVERFLOW);
|
|
|
|
if (soc_is_am33xx() || soc_is_am43xx()) {
|
|
clkevt->dev.suspend = omap_clkevt_idle;
|
|
clkevt->dev.resume = omap_clkevt_unidle;
|
|
|
|
clockevent_gpt_hwmod =
|
|
omap_hwmod_lookup(clkevt->dev.name);
|
|
}
|
|
|
|
pr_info("OMAP clockevent source: %s at %lu Hz\n", clkevt->dev.name,
|
|
timer->rate);
|
|
}
|
|
|
|
/* Clocksource code */
|
|
static struct omap_dm_timer clksrc;
|
|
static bool use_gptimer_clksrc __initdata;
|
|
|
|
/*
|
|
* clocksource
|
|
*/
|
|
static u64 clocksource_read_cycles(struct clocksource *cs)
|
|
{
|
|
return (u64)__omap_dm_timer_read_counter(&clksrc,
|
|
OMAP_TIMER_NONPOSTED);
|
|
}
|
|
|
|
static struct clocksource clocksource_gpt = {
|
|
.rating = 300,
|
|
.read = clocksource_read_cycles,
|
|
.mask = CLOCKSOURCE_MASK(32),
|
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
};
|
|
|
|
static u64 notrace dmtimer_read_sched_clock(void)
|
|
{
|
|
if (clksrc.reserved)
|
|
return __omap_dm_timer_read_counter(&clksrc,
|
|
OMAP_TIMER_NONPOSTED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id omap_counter_match[] __initconst = {
|
|
{ .compatible = "ti,omap-counter32k", },
|
|
{ }
|
|
};
|
|
|
|
/* Setup free-running counter for clocksource */
|
|
static int __init __maybe_unused omap2_sync32k_clocksource_init(void)
|
|
{
|
|
int ret;
|
|
struct device_node *np = NULL;
|
|
struct omap_hwmod *oh;
|
|
const char *oh_name = "counter_32k";
|
|
|
|
/*
|
|
* See if the 32kHz counter is supported.
|
|
*/
|
|
np = omap_get_timer_dt(omap_counter_match, NULL);
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
of_property_read_string_index(np->parent, "ti,hwmods", 0, &oh_name);
|
|
if (!oh_name) {
|
|
of_property_read_string_index(np, "ti,hwmods", 0, &oh_name);
|
|
if (!oh_name)
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* First check hwmod data is available for sync32k counter
|
|
*/
|
|
oh = omap_hwmod_lookup(oh_name);
|
|
if (!oh || oh->slaves_cnt == 0)
|
|
return -ENODEV;
|
|
|
|
omap_hwmod_setup_one(oh_name);
|
|
|
|
ret = omap_hwmod_enable(oh);
|
|
if (ret) {
|
|
pr_warn("%s: failed to enable counter_32k module (%d)\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int omap2_gptimer_clksrc_load;
|
|
|
|
static void omap2_gptimer_clksrc_suspend(struct clocksource *unused)
|
|
{
|
|
omap2_gptimer_clksrc_load =
|
|
__omap_dm_timer_read_counter(&clksrc, OMAP_TIMER_NONPOSTED);
|
|
|
|
omap_hwmod_idle(clocksource_gpt_hwmod);
|
|
}
|
|
|
|
static void omap2_gptimer_clksrc_resume(struct clocksource *unused)
|
|
{
|
|
omap_hwmod_enable(clocksource_gpt_hwmod);
|
|
|
|
__omap_dm_timer_load_start(&clksrc,
|
|
OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR,
|
|
omap2_gptimer_clksrc_load,
|
|
OMAP_TIMER_NONPOSTED);
|
|
}
|
|
|
|
static void __init omap2_gptimer_clocksource_init(int gptimer_id,
|
|
const char *fck_source,
|
|
const char *property)
|
|
{
|
|
int res;
|
|
|
|
clksrc.id = gptimer_id;
|
|
clksrc.errata = omap_dm_timer_get_errata();
|
|
|
|
res = omap_dm_timer_init_one(&clksrc, fck_source, property,
|
|
&clocksource_gpt.name,
|
|
OMAP_TIMER_NONPOSTED);
|
|
|
|
if (soc_is_am43xx()) {
|
|
clocksource_gpt.suspend = omap2_gptimer_clksrc_suspend;
|
|
clocksource_gpt.resume = omap2_gptimer_clksrc_resume;
|
|
|
|
clocksource_gpt_hwmod =
|
|
omap_hwmod_lookup(clocksource_gpt.name);
|
|
}
|
|
|
|
BUG_ON(res);
|
|
|
|
__omap_dm_timer_load_start(&clksrc,
|
|
OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0,
|
|
OMAP_TIMER_NONPOSTED);
|
|
sched_clock_register(dmtimer_read_sched_clock, 32, clksrc.rate);
|
|
|
|
if (clocksource_register_hz(&clocksource_gpt, clksrc.rate))
|
|
pr_err("Could not register clocksource %s\n",
|
|
clocksource_gpt.name);
|
|
else
|
|
pr_info("OMAP clocksource: %s at %lu Hz\n",
|
|
clocksource_gpt.name, clksrc.rate);
|
|
}
|
|
|
|
static void __init __omap_sync32k_timer_init(int clkev_nr, const char *clkev_src,
|
|
const char *clkev_prop, int clksrc_nr, const char *clksrc_src,
|
|
const char *clksrc_prop, bool gptimer)
|
|
{
|
|
omap_clk_init();
|
|
omap_dmtimer_init();
|
|
dmtimer_clkevt_init_common(&clockevent, clkev_nr, clkev_src,
|
|
CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
|
cpu_possible_mask, clkev_prop, 300, "clockevent");
|
|
clockevents_config_and_register(&clockevent.dev, clockevent.timer.rate,
|
|
3, /* Timer internal resynch latency */
|
|
0xffffffff);
|
|
|
|
/* Enable the use of clocksource="gp_timer" kernel parameter */
|
|
if (use_gptimer_clksrc || gptimer)
|
|
omap2_gptimer_clocksource_init(clksrc_nr, clksrc_src,
|
|
clksrc_prop);
|
|
else
|
|
omap2_sync32k_clocksource_init();
|
|
}
|
|
|
|
void __init omap_init_time(void)
|
|
{
|
|
__omap_sync32k_timer_init(1, "timer_32k_ck", "ti,timer-alwon",
|
|
2, "timer_sys_ck", NULL, false);
|
|
|
|
timer_probe();
|
|
}
|
|
|
|
#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM43XX)
|
|
void __init omap3_secure_sync32k_timer_init(void)
|
|
{
|
|
__omap_sync32k_timer_init(12, "secure_32k_fck", "ti,timer-secure",
|
|
2, "timer_sys_ck", NULL, false);
|
|
|
|
timer_probe();
|
|
}
|
|
#endif /* CONFIG_ARCH_OMAP3 */
|
|
|
|
#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) || \
|
|
defined(CONFIG_SOC_AM43XX)
|
|
void __init omap3_gptimer_timer_init(void)
|
|
{
|
|
__omap_sync32k_timer_init(2, "timer_sys_ck", NULL,
|
|
1, "timer_sys_ck", "ti,timer-alwon", true);
|
|
if (of_have_populated_dt())
|
|
timer_probe();
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
|
|
defined(CONFIG_SOC_DRA7XX)
|
|
static void __init omap4_sync32k_timer_init(void)
|
|
{
|
|
__omap_sync32k_timer_init(1, "timer_32k_ck", "ti,timer-alwon",
|
|
2, "sys_clkin_ck", NULL, false);
|
|
}
|
|
|
|
void __init omap4_local_timer_init(void)
|
|
{
|
|
omap4_sync32k_timer_init();
|
|
timer_probe();
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX)
|
|
|
|
/*
|
|
* The realtime counter also called master counter, is a free-running
|
|
* counter, which is related to real time. It produces the count used
|
|
* by the CPU local timer peripherals in the MPU cluster. The timer counts
|
|
* at a rate of 6.144 MHz. Because the device operates on different clocks
|
|
* in different power modes, the master counter shifts operation between
|
|
* clocks, adjusting the increment per clock in hardware accordingly to
|
|
* maintain a constant count rate.
|
|
*/
|
|
static void __init realtime_counter_init(void)
|
|
{
|
|
#ifdef CONFIG_SOC_HAS_REALTIME_COUNTER
|
|
void __iomem *base;
|
|
static struct clk *sys_clk;
|
|
unsigned long rate;
|
|
unsigned int reg;
|
|
unsigned long long num, den;
|
|
|
|
base = ioremap(REALTIME_COUNTER_BASE, SZ_32);
|
|
if (!base) {
|
|
pr_err("%s: ioremap failed\n", __func__);
|
|
return;
|
|
}
|
|
sys_clk = clk_get(NULL, "sys_clkin");
|
|
if (IS_ERR(sys_clk)) {
|
|
pr_err("%s: failed to get system clock handle\n", __func__);
|
|
iounmap(base);
|
|
return;
|
|
}
|
|
|
|
rate = clk_get_rate(sys_clk);
|
|
clk_put(sys_clk);
|
|
|
|
if (soc_is_dra7xx()) {
|
|
/*
|
|
* Errata i856 says the 32.768KHz crystal does not start at
|
|
* power on, so the CPU falls back to an emulated 32KHz clock
|
|
* based on sysclk / 610 instead. This causes the master counter
|
|
* frequency to not be 6.144MHz but at sysclk / 610 * 375 / 2
|
|
* (OR sysclk * 75 / 244)
|
|
*
|
|
* This affects at least the DRA7/AM572x 1.0, 1.1 revisions.
|
|
* Of course any board built without a populated 32.768KHz
|
|
* crystal would also need this fix even if the CPU is fixed
|
|
* later.
|
|
*
|
|
* Either case can be detected by using the two speedselect bits
|
|
* If they are not 0, then the 32.768KHz clock driving the
|
|
* coarse counter that corrects the fine counter every time it
|
|
* ticks is actually rate/610 rather than 32.768KHz and we
|
|
* should compensate to avoid the 570ppm (at 20MHz, much worse
|
|
* at other rates) too fast system time.
|
|
*/
|
|
reg = omap_ctrl_readl(DRA7_CTRL_CORE_BOOTSTRAP);
|
|
if (reg & DRA7_SPEEDSELECT_MASK) {
|
|
num = 75;
|
|
den = 244;
|
|
goto sysclk1_based;
|
|
}
|
|
}
|
|
|
|
/* Numerator/denumerator values refer TRM Realtime Counter section */
|
|
switch (rate) {
|
|
case 12000000:
|
|
num = 64;
|
|
den = 125;
|
|
break;
|
|
case 13000000:
|
|
num = 768;
|
|
den = 1625;
|
|
break;
|
|
case 19200000:
|
|
num = 8;
|
|
den = 25;
|
|
break;
|
|
case 20000000:
|
|
num = 192;
|
|
den = 625;
|
|
break;
|
|
case 26000000:
|
|
num = 384;
|
|
den = 1625;
|
|
break;
|
|
case 27000000:
|
|
num = 256;
|
|
den = 1125;
|
|
break;
|
|
case 38400000:
|
|
default:
|
|
/* Program it for 38.4 MHz */
|
|
num = 4;
|
|
den = 25;
|
|
break;
|
|
}
|
|
|
|
sysclk1_based:
|
|
/* Program numerator and denumerator registers */
|
|
reg = readl_relaxed(base + INCREMENTER_NUMERATOR_OFFSET) &
|
|
NUMERATOR_DENUMERATOR_MASK;
|
|
reg |= num;
|
|
writel_relaxed(reg, base + INCREMENTER_NUMERATOR_OFFSET);
|
|
|
|
reg = readl_relaxed(base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET) &
|
|
NUMERATOR_DENUMERATOR_MASK;
|
|
reg |= den;
|
|
writel_relaxed(reg, base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET);
|
|
|
|
arch_timer_freq = DIV_ROUND_UP_ULL(rate * num, den);
|
|
set_cntfreq();
|
|
|
|
iounmap(base);
|
|
#endif
|
|
}
|
|
|
|
void __init omap5_realtime_timer_init(void)
|
|
{
|
|
omap4_sync32k_timer_init();
|
|
realtime_counter_init();
|
|
|
|
timer_probe();
|
|
}
|
|
#endif /* CONFIG_SOC_OMAP5 || CONFIG_SOC_DRA7XX */
|
|
|
|
/**
|
|
* omap2_override_clocksource - clocksource override with user configuration
|
|
*
|
|
* Allows user to override default clocksource, using kernel parameter
|
|
* clocksource="gp_timer" (For all OMAP2PLUS architectures)
|
|
*
|
|
* Note that, here we are using same standard kernel parameter "clocksource=",
|
|
* and not introducing any OMAP specific interface.
|
|
*/
|
|
static int __init omap2_override_clocksource(char *str)
|
|
{
|
|
if (!str)
|
|
return 0;
|
|
/*
|
|
* For OMAP architecture, we only have two options
|
|
* - sync_32k (default)
|
|
* - gp_timer (sys_clk based)
|
|
*/
|
|
if (!strcmp(str, "gp_timer"))
|
|
use_gptimer_clksrc = true;
|
|
|
|
return 0;
|
|
}
|
|
early_param("clocksource", omap2_override_clocksource);
|