Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
18a2078
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
Kconfig
Makefile
acpi_pm.c
arc_timer.c
arm_arch_timer.c
arm_global_timer.c
armv7m_systick.c
asm9260_timer.c
bcm2835_timer.c
bcm_kona_timer.c
clksrc-dbx500-prcmu.c
clksrc_st_lpc.c
clps711x-timer.c
dummy_timer.c
dw_apb_timer.c
dw_apb_timer_of.c
em_sti.c
exynos_mct.c
hyperv_timer.c
i8253.c
ingenic-ost.c
ingenic-sysost.c
ingenic-timer.c
jcore-pit.c
mips-gic-timer.c
mmio.c
mps2-timer.c
mxs_timer.c
nomadik-mtu.c
numachip.c
renesas-ostm.c
samsung_pwm_timer.c
scx200_hrt.c
sh_cmt.c
sh_mtu2.c
sh_tmu.c
timer-armada-370-xp.c
timer-atmel-pit.c
timer-atmel-st.c
timer-atmel-tcb.c
timer-cadence-ttc.c
timer-clint.c
timer-cs5535.c
timer-davinci.c
timer-digicolor.c
timer-fsl-ftm.c
timer-fttmr010.c
timer-goldfish.c
timer-gx6605s.c
timer-gxp.c
timer-imx-gpt.c
timer-imx-sysctr.c
timer-imx-tpm.c
timer-integrator-ap.c
timer-ixp4xx.c
timer-keystone.c
timer-lpc32xx.c
timer-mediatek.c
timer-meson6.c
timer-microchip-pit64b.c
timer-milbeaut.c
timer-mp-csky.c
timer-msc313e.c
timer-npcm7xx.c
timer-of.c
timer-of.h
timer-orion.c
timer-owl.c
timer-oxnas-rps.c
timer-pistachio.c
timer-probe.c
timer-pxa.c
timer-qcom.c
timer-rda.c
timer-riscv.c
timer-rockchip.c
timer-sp.h
timer-sp804.c
timer-sprd.c
timer-stm32-lp.c
timer-stm32.c
timer-sun4i.c
timer-sun5i.c
timer-tegra.c
timer-tegra186.c
timer-ti-32k.c
timer-ti-dm-systimer.c
timer-ti-dm.c
timer-versatile.c
timer-vf-pit.c
timer-vt8500.c
timer-zevio.c
comedi
connector
counter
cpufreq
cpuidle
crypto
cxl
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hte
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
parisc
parport
pci
pcmcia
peci
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
ufs
uio
usb
vdpa
vfio
vhost
video
virt
virtio
vlynq
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
clocksource
/
timer-ti-dm.c
Blame
Blame
Latest commit
History
History
1287 lines (1050 loc) · 30 KB
Breadcrumbs
linux
/
drivers
/
clocksource
/
timer-ti-dm.c
Top
File metadata and controls
Code
Blame
1287 lines (1050 loc) · 30 KB
Raw
// SPDX-License-Identifier: GPL-2.0+ /* * linux/arch/arm/plat-omap/dmtimer.c * * OMAP Dual-Mode Timers * * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ * Tarun Kanti DebBarma <tarun.kanti@ti.com> * Thara Gopinath <thara@ti.com> * * dmtimer adaptation to platform_driver. * * Copyright (C) 2005 Nokia Corporation * OMAP2 support by Juha Yrjola * API improvements and OMAP2 clock framework support by Timo Teras * * Copyright (C) 2009 Texas Instruments * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> */ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/cpu_pm.h> #include <linux/module.h> #include <linux/io.h> #include <linux/device.h> #include <linux/err.h> #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/platform_data/dmtimer-omap.h> #include <clocksource/timer-ti-dm.h> /* * timer errata flags * * Errata i103/i767 impacts all OMAP3/4/5 devices including AM33xx. This * errata prevents us from using posted mode on these devices, unless the * timer counter register is never read. For more details please refer to * the OMAP3/4/5 errata documents. */ #define OMAP_TIMER_ERRATA_I103_I767 0x80000000 /* posted mode types */ #define OMAP_TIMER_NONPOSTED 0x00 #define OMAP_TIMER_POSTED 0x01 /* register offsets with the write pending bit encoded */ #define WPSHIFT 16 #define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \ | (WP_NONE << WPSHIFT)) #define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \ | (WP_TCLR << WPSHIFT)) #define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \ | (WP_TCRR << WPSHIFT)) #define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \ | (WP_TLDR << WPSHIFT)) #define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \ | (WP_TTGR << WPSHIFT)) #define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \ | (WP_NONE << WPSHIFT)) #define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \ | (WP_TMAR << WPSHIFT)) #define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \ | (WP_NONE << WPSHIFT)) #define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \ | (WP_NONE << WPSHIFT)) #define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \ | (WP_NONE << WPSHIFT)) #define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \ | (WP_TPIR << WPSHIFT)) #define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \ | (WP_TNIR << WPSHIFT)) #define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \ | (WP_TCVR << WPSHIFT)) #define OMAP_TIMER_TICK_INT_MASK_SET_REG \ (_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT)) #define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \ (_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT)) struct timer_regs { u32 ocp_cfg; u32 tidr; u32 tier; u32 twer; u32 tclr; u32 tcrr; u32 tldr; u32 ttrg; u32 twps; u32 tmar; u32 tcar1; u32 tsicr; u32 tcar2; u32 tpir; u32 tnir; u32 tcvr; u32 tocr; u32 towr; }; struct dmtimer { struct omap_dm_timer cookie; int id; int irq; struct clk *fclk; void __iomem *io_base; int irq_stat; /* TISR/IRQSTATUS interrupt status */ int irq_ena; /* irq enable */ int irq_dis; /* irq disable, only on v2 ip */ void __iomem *pend; /* write pending */ void __iomem *func_base; /* function register base */ atomic_t enabled; unsigned long rate; unsigned reserved:1; unsigned posted:1; unsigned omap1:1; struct timer_regs context; int revision; u32 capability; u32 errata; struct platform_device *pdev; struct list_head node; struct notifier_block nb; }; static u32 omap_reserved_systimers; static LIST_HEAD(omap_timer_list); static DEFINE_SPINLOCK(dm_timer_lock); enum { REQUEST_ANY = 0, REQUEST_BY_ID, REQUEST_BY_CAP, REQUEST_BY_NODE, }; /** * dmtimer_read - read timer registers in posted and non-posted mode * @timer: timer pointer over which read operation to perform * @reg: lowest byte holds the register offset * * The posted mode bit is encoded in reg. Note that in posted mode, write * pending bit must be checked. Otherwise a read of a non completed write * will produce an error. */ static inline u32 dmtimer_read(struct dmtimer *timer, u32 reg) { u16 wp, offset; wp = reg >> WPSHIFT; offset = reg & 0xff; /* Wait for a possible write pending bit in posted mode */ if (wp && timer->posted) while (readl_relaxed(timer->pend) & wp) cpu_relax(); return readl_relaxed(timer->func_base + offset); } /** * dmtimer_write - write timer registers in posted and non-posted mode * @timer: timer pointer over which write operation is to perform * @reg: lowest byte holds the register offset * @value: data to write into the register * * The posted mode bit is encoded in reg. Note that in posted mode, the write * pending bit must be checked. Otherwise a write on a register which has a * pending write will be lost. */ static inline void dmtimer_write(struct dmtimer *timer, u32 reg, u32 val) { u16 wp, offset; wp = reg >> WPSHIFT; offset = reg & 0xff; /* Wait for a possible write pending bit in posted mode */ if (wp && timer->posted) while (readl_relaxed(timer->pend) & wp) cpu_relax(); writel_relaxed(val, timer->func_base + offset); } static inline void __omap_dm_timer_init_regs(struct dmtimer *timer) { u32 tidr; /* Assume v1 ip if bits [31:16] are zero */ tidr = readl_relaxed(timer->io_base); if (!(tidr >> 16)) { timer->revision = 1; timer->irq_stat = OMAP_TIMER_V1_STAT_OFFSET; timer->irq_ena = OMAP_TIMER_V1_INT_EN_OFFSET; timer->irq_dis = OMAP_TIMER_V1_INT_EN_OFFSET; timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET; timer->func_base = timer->io_base; } else { timer->revision = 2; timer->irq_stat = OMAP_TIMER_V2_IRQSTATUS - OMAP_TIMER_V2_FUNC_OFFSET; timer->irq_ena = OMAP_TIMER_V2_IRQENABLE_SET - OMAP_TIMER_V2_FUNC_OFFSET; timer->irq_dis = OMAP_TIMER_V2_IRQENABLE_CLR - OMAP_TIMER_V2_FUNC_OFFSET; timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET + OMAP_TIMER_V2_FUNC_OFFSET; timer->func_base = timer->io_base + OMAP_TIMER_V2_FUNC_OFFSET; } } /* * __omap_dm_timer_enable_posted - enables write posted mode * @timer: pointer to timer instance handle * * Enables the write posted mode for the timer. When posted mode is enabled * writes to certain timer registers are immediately acknowledged by the * internal bus and hence prevents stalling the CPU waiting for the write to * complete. Enabling this feature can improve performance for writing to the * timer registers. */ static inline void __omap_dm_timer_enable_posted(struct dmtimer *timer) { if (timer->posted) return; if (timer->errata & OMAP_TIMER_ERRATA_I103_I767) { timer->posted = OMAP_TIMER_NONPOSTED; dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0); return; } dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, OMAP_TIMER_CTRL_POSTED); timer->context.tsicr = OMAP_TIMER_CTRL_POSTED; timer->posted = OMAP_TIMER_POSTED; } static inline void __omap_dm_timer_stop(struct dmtimer *timer, unsigned long rate) { u32 l; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); if (l & OMAP_TIMER_CTRL_ST) { l &= ~0x1; dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); #ifdef CONFIG_ARCH_OMAP2PLUS /* Readback to make sure write has completed */ dmtimer_read(timer, OMAP_TIMER_CTRL_REG); /* * Wait for functional clock period x 3.5 to make sure that * timer is stopped */ udelay(3500000 / rate + 1); #endif } /* Ack possibly pending interrupt */ dmtimer_write(timer, timer->irq_stat, OMAP_TIMER_INT_OVERFLOW); } static inline void __omap_dm_timer_int_enable(struct dmtimer *timer, unsigned int value) { dmtimer_write(timer, timer->irq_ena, value); dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value); } static inline unsigned int __omap_dm_timer_read_counter(struct dmtimer *timer) { return dmtimer_read(timer, OMAP_TIMER_COUNTER_REG); } static inline void __omap_dm_timer_write_status(struct dmtimer *timer, unsigned int value) { dmtimer_write(timer, timer->irq_stat, value); } static void omap_timer_restore_context(struct dmtimer *timer) { dmtimer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, timer->context.ocp_cfg); dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, timer->context.twer); dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, timer->context.tcrr); dmtimer_write(timer, OMAP_TIMER_LOAD_REG, timer->context.tldr); dmtimer_write(timer, OMAP_TIMER_MATCH_REG, timer->context.tmar); dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, timer->context.tsicr); dmtimer_write(timer, timer->irq_ena, timer->context.tier); dmtimer_write(timer, OMAP_TIMER_CTRL_REG, timer->context.tclr); } static void omap_timer_save_context(struct dmtimer *timer) { timer->context.ocp_cfg = dmtimer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET); timer->context.tclr = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); timer->context.twer = dmtimer_read(timer, OMAP_TIMER_WAKEUP_EN_REG); timer->context.tldr = dmtimer_read(timer, OMAP_TIMER_LOAD_REG); timer->context.tmar = dmtimer_read(timer, OMAP_TIMER_MATCH_REG); timer->context.tier = dmtimer_read(timer, timer->irq_ena); timer->context.tsicr = dmtimer_read(timer, OMAP_TIMER_IF_CTRL_REG); } static int omap_timer_context_notifier(struct notifier_block *nb, unsigned long cmd, void *v) { struct dmtimer *timer; timer = container_of(nb, struct dmtimer, nb); switch (cmd) { case CPU_CLUSTER_PM_ENTER: if ((timer->capability & OMAP_TIMER_ALWON) || !atomic_read(&timer->enabled)) break; omap_timer_save_context(timer); break; case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */ break; case CPU_CLUSTER_PM_EXIT: if ((timer->capability & OMAP_TIMER_ALWON) || !atomic_read(&timer->enabled)) break; omap_timer_restore_context(timer); break; } return NOTIFY_OK; } static int omap_dm_timer_reset(struct dmtimer *timer) { u32 l, timeout = 100000; if (timer->revision != 1) return -EINVAL; dmtimer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); do { l = dmtimer_read(timer, OMAP_TIMER_V1_SYS_STAT_OFFSET); } while (!l && timeout--); if (!timeout) { dev_err(&timer->pdev->dev, "Timer failed to reset\n"); return -ETIMEDOUT; } /* Configure timer for smart-idle mode */ l = dmtimer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET); l |= 0x2 << 0x3; dmtimer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l); timer->posted = 0; return 0; } /* * Functions exposed to PWM and remoteproc drivers via platform_data. * Do not use these in the driver, these will get deprecated and will * will be replaced by Linux generic framework functions such as * chained interrupts and clock framework. */ static struct dmtimer *to_dmtimer(struct omap_dm_timer *cookie) { if (!cookie) return NULL; return container_of(cookie, struct dmtimer, cookie); } static int omap_dm_timer_set_source(struct omap_dm_timer *cookie, int source) { int ret; const char *parent_name; struct clk *parent; struct dmtimer_platform_data *pdata; struct dmtimer *timer; timer = to_dmtimer(cookie); if (unlikely(!timer) || IS_ERR(timer->fclk)) return -EINVAL; switch (source) { case OMAP_TIMER_SRC_SYS_CLK: parent_name = "timer_sys_ck"; break; case OMAP_TIMER_SRC_32_KHZ: parent_name = "timer_32k_ck"; break; case OMAP_TIMER_SRC_EXT_CLK: parent_name = "timer_ext_ck"; break; default: return -EINVAL; } pdata = timer->pdev->dev.platform_data; /* * FIXME: Used for OMAP1 devices only because they do not currently * use the clock framework to set the parent clock. To be removed * once OMAP1 migrated to using clock framework for dmtimers */ if (timer->omap1 && pdata && pdata->set_timer_src) return pdata->set_timer_src(timer->pdev, source); #if defined(CONFIG_COMMON_CLK) /* Check if the clock has configurable parents */ if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2) return 0; #endif parent = clk_get(&timer->pdev->dev, parent_name); if (IS_ERR(parent)) { pr_err("%s: %s not found\n", __func__, parent_name); return -EINVAL; } ret = clk_set_parent(timer->fclk, parent); if (ret < 0) pr_err("%s: failed to set %s as parent\n", __func__, parent_name); clk_put(parent); return ret; } static void omap_dm_timer_enable(struct omap_dm_timer *cookie) { struct dmtimer *timer = to_dmtimer(cookie); struct device *dev = &timer->pdev->dev; int rc; rc = pm_runtime_resume_and_get(dev); if (rc) dev_err(dev, "could not enable timer\n"); } static void omap_dm_timer_disable(struct omap_dm_timer *cookie) { struct dmtimer *timer = to_dmtimer(cookie); struct device *dev = &timer->pdev->dev; pm_runtime_put_sync(dev); } static int omap_dm_timer_prepare(struct dmtimer *timer) { struct device *dev = &timer->pdev->dev; int rc; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; if (timer->capability & OMAP_TIMER_NEEDS_RESET) { rc = omap_dm_timer_reset(timer); if (rc) { pm_runtime_put_sync(dev); return rc; } } __omap_dm_timer_enable_posted(timer); pm_runtime_put_sync(dev); return 0; } static inline u32 omap_dm_timer_reserved_systimer(int id) { return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0; } static struct dmtimer *_omap_dm_timer_request(int req_type, void *data) { struct dmtimer *timer = NULL, *t; struct device_node *np = NULL; unsigned long flags; u32 cap = 0; int id = 0; switch (req_type) { case REQUEST_BY_ID: id = *(int *)data; break; case REQUEST_BY_CAP: cap = *(u32 *)data; break; case REQUEST_BY_NODE: np = (struct device_node *)data; break; default: /* REQUEST_ANY */ break; } spin_lock_irqsave(&dm_timer_lock, flags); list_for_each_entry(t, &omap_timer_list, node) { if (t->reserved) continue; switch (req_type) { case REQUEST_BY_ID: if (id == t->pdev->id) { timer = t; timer->reserved = 1; goto found; } break; case REQUEST_BY_CAP: if (cap == (t->capability & cap)) { /* * If timer is not NULL, we have already found * one timer. But it was not an exact match * because it had more capabilities than what * was required. Therefore, unreserve the last * timer found and see if this one is a better * match. */ if (timer) timer->reserved = 0; timer = t; timer->reserved = 1; /* Exit loop early if we find an exact match */ if (t->capability == cap) goto found; } break; case REQUEST_BY_NODE: if (np == t->pdev->dev.of_node) { timer = t; timer->reserved = 1; goto found; } break; default: /* REQUEST_ANY */ timer = t; timer->reserved = 1; goto found; } } found: spin_unlock_irqrestore(&dm_timer_lock, flags); if (timer && omap_dm_timer_prepare(timer)) { timer->reserved = 0; timer = NULL; } if (!timer) pr_debug("%s: timer request failed!\n", __func__); return timer; } static struct omap_dm_timer *omap_dm_timer_request(void) { struct dmtimer *timer; timer = _omap_dm_timer_request(REQUEST_ANY, NULL); if (!timer) return NULL; return &timer->cookie; } static struct omap_dm_timer *omap_dm_timer_request_specific(int id) { struct dmtimer *timer; /* Requesting timer by ID is not supported when device tree is used */ if (of_have_populated_dt()) { pr_warn("%s: Please use omap_dm_timer_request_by_node()\n", __func__); return NULL; } timer = _omap_dm_timer_request(REQUEST_BY_ID, &id); if (!timer) return NULL; return &timer->cookie; } /** * omap_dm_timer_request_by_node - Request a timer by device-tree node * @np: Pointer to device-tree timer node * * Request a timer based upon a device node pointer. Returns pointer to * timer handle on success and a NULL pointer on failure. */ static struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) { struct dmtimer *timer; if (!np) return NULL; timer = _omap_dm_timer_request(REQUEST_BY_NODE, np); if (!timer) return NULL; return &timer->cookie; } static int omap_dm_timer_free(struct omap_dm_timer *cookie) { struct dmtimer *timer; struct device *dev; int rc; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; WARN_ON(!timer->reserved); timer->reserved = 0; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; /* Clear timer configuration */ dmtimer_write(timer, OMAP_TIMER_CTRL_REG, 0); pm_runtime_put_sync(dev); return 0; } static int omap_dm_timer_get_irq(struct omap_dm_timer *cookie) { struct dmtimer *timer = to_dmtimer(cookie); if (timer) return timer->irq; return -EINVAL; } #if defined(CONFIG_ARCH_OMAP1) #include <linux/soc/ti/omap1-io.h> static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *cookie) { return NULL; } /** * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR * @inputmask: current value of idlect mask */ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) { int i = 0; struct dmtimer *timer = NULL; unsigned long flags; /* If ARMXOR cannot be idled this function call is unnecessary */ if (!(inputmask & (1 << 1))) return inputmask; /* If any active timer is using ARMXOR return modified mask */ spin_lock_irqsave(&dm_timer_lock, flags); list_for_each_entry(timer, &omap_timer_list, node) { u32 l; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); if (l & OMAP_TIMER_CTRL_ST) { if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) inputmask &= ~(1 << 1); else inputmask &= ~(1 << 2); } i++; } spin_unlock_irqrestore(&dm_timer_lock, flags); return inputmask; } #else static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *cookie) { struct dmtimer *timer = to_dmtimer(cookie); if (timer && !IS_ERR(timer->fclk)) return timer->fclk; return NULL; } __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) { BUG(); return 0; } #endif static int omap_dm_timer_start(struct omap_dm_timer *cookie) { struct dmtimer *timer; struct device *dev; int rc; u32 l; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); if (!(l & OMAP_TIMER_CTRL_ST)) { l |= OMAP_TIMER_CTRL_ST; dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); } return 0; } static int omap_dm_timer_stop(struct omap_dm_timer *cookie) { struct dmtimer *timer; struct device *dev; unsigned long rate = 0; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; if (!timer->omap1) rate = clk_get_rate(timer->fclk); __omap_dm_timer_stop(timer, rate); pm_runtime_put_sync(dev); return 0; } static int omap_dm_timer_set_load(struct omap_dm_timer *cookie, unsigned int load) { struct dmtimer *timer; struct device *dev; int rc; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; dmtimer_write(timer, OMAP_TIMER_LOAD_REG, load); pm_runtime_put_sync(dev); return 0; } static int omap_dm_timer_set_match(struct omap_dm_timer *cookie, int enable, unsigned int match) { struct dmtimer *timer; struct device *dev; int rc; u32 l; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); if (enable) l |= OMAP_TIMER_CTRL_CE; else l &= ~OMAP_TIMER_CTRL_CE; dmtimer_write(timer, OMAP_TIMER_MATCH_REG, match); dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); pm_runtime_put_sync(dev); return 0; } static int omap_dm_timer_set_pwm(struct omap_dm_timer *cookie, int def_on, int toggle, int trigger, int autoreload) { struct dmtimer *timer; struct device *dev; int rc; u32 l; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | OMAP_TIMER_CTRL_PT | (0x03 << 10) | OMAP_TIMER_CTRL_AR); if (def_on) l |= OMAP_TIMER_CTRL_SCPWM; if (toggle) l |= OMAP_TIMER_CTRL_PT; l |= trigger << 10; if (autoreload) l |= OMAP_TIMER_CTRL_AR; dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); pm_runtime_put_sync(dev); return 0; } static int omap_dm_timer_get_pwm_status(struct omap_dm_timer *cookie) { struct dmtimer *timer; struct device *dev; int rc; u32 l; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); pm_runtime_put_sync(dev); return l; } static int omap_dm_timer_set_prescaler(struct omap_dm_timer *cookie, int prescaler) { struct dmtimer *timer; struct device *dev; int rc; u32 l; timer = to_dmtimer(cookie); if (unlikely(!timer) || prescaler < -1 || prescaler > 7) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; l = dmtimer_read(timer, OMAP_TIMER_CTRL_REG); l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); if (prescaler >= 0) { l |= OMAP_TIMER_CTRL_PRE; l |= prescaler << 2; } dmtimer_write(timer, OMAP_TIMER_CTRL_REG, l); pm_runtime_put_sync(dev); return 0; } static int omap_dm_timer_set_int_enable(struct omap_dm_timer *cookie, unsigned int value) { struct dmtimer *timer; struct device *dev; int rc; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; __omap_dm_timer_int_enable(timer, value); pm_runtime_put_sync(dev); return 0; } /** * omap_dm_timer_set_int_disable - disable timer interrupts * @timer: pointer to timer handle * @mask: bit mask of interrupts to be disabled * * Disables the specified timer interrupts for a timer. */ static int omap_dm_timer_set_int_disable(struct omap_dm_timer *cookie, u32 mask) { struct dmtimer *timer; struct device *dev; u32 l = mask; int rc; timer = to_dmtimer(cookie); if (unlikely(!timer)) return -EINVAL; dev = &timer->pdev->dev; rc = pm_runtime_resume_and_get(dev); if (rc) return rc; if (timer->revision == 1) l = dmtimer_read(timer, timer->irq_ena) & ~mask; dmtimer_write(timer, timer->irq_dis, l); l = dmtimer_read(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; dmtimer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, l); pm_runtime_put_sync(dev); return 0; } static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *cookie) { struct dmtimer *timer; unsigned int l; timer = to_dmtimer(cookie); if (unlikely(!timer || !atomic_read(&timer->enabled))) { pr_err("%s: timer not available or enabled.\n", __func__); return 0; } l = dmtimer_read(timer, timer->irq_stat); return l; } static int omap_dm_timer_write_status(struct omap_dm_timer *cookie, unsigned int value) { struct dmtimer *timer; timer = to_dmtimer(cookie); if (unlikely(!timer || !atomic_read(&timer->enabled))) return -EINVAL; __omap_dm_timer_write_status(timer, value); return 0; } static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *cookie) { struct dmtimer *timer; timer = to_dmtimer(cookie); if (unlikely(!timer || !atomic_read(&timer->enabled))) { pr_err("%s: timer not iavailable or enabled.\n", __func__); return 0; } return __omap_dm_timer_read_counter(timer); } static int omap_dm_timer_write_counter(struct omap_dm_timer *cookie, unsigned int value) { struct dmtimer *timer; timer = to_dmtimer(cookie); if (unlikely(!timer || !atomic_read(&timer->enabled))) { pr_err("%s: timer not available or enabled.\n", __func__); return -EINVAL; } dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, value); /* Save the context */ timer->context.tcrr = value; return 0; } static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev) { struct dmtimer *timer = dev_get_drvdata(dev); atomic_set(&timer->enabled, 0); if (timer->capability & OMAP_TIMER_ALWON || !timer->func_base) return 0; omap_timer_save_context(timer); return 0; } static int __maybe_unused omap_dm_timer_runtime_resume(struct device *dev) { struct dmtimer *timer = dev_get_drvdata(dev); if (!(timer->capability & OMAP_TIMER_ALWON) && timer->func_base) omap_timer_restore_context(timer); atomic_set(&timer->enabled, 1); return 0; } static const struct dev_pm_ops omap_dm_timer_pm_ops = { SET_RUNTIME_PM_OPS(omap_dm_timer_runtime_suspend, omap_dm_timer_runtime_resume, NULL) }; static const struct of_device_id omap_timer_match[]; /** * omap_dm_timer_probe - probe function called for every registered device * @pdev: pointer to current timer platform device * * Called by driver framework at the end of device registration for all * timer devices. */ static int omap_dm_timer_probe(struct platform_device *pdev) { unsigned long flags; struct dmtimer *timer; struct device *dev = &pdev->dev; const struct dmtimer_platform_data *pdata; int ret; pdata = of_device_get_match_data(dev); if (!pdata) pdata = dev_get_platdata(dev); else dev->platform_data = (void *)pdata; if (!pdata) { dev_err(dev, "%s: no platform data.\n", __func__); return -ENODEV; } timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); if (!timer) return -ENOMEM; timer->irq = platform_get_irq(pdev, 0); if (timer->irq < 0) return timer->irq; timer->io_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(timer->io_base)) return PTR_ERR(timer->io_base); platform_set_drvdata(pdev, timer); if (dev->of_node) { if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) timer->capability |= OMAP_TIMER_ALWON; if (of_find_property(dev->of_node, "ti,timer-dsp", NULL)) timer->capability |= OMAP_TIMER_HAS_DSP_IRQ; if (of_find_property(dev->of_node, "ti,timer-pwm", NULL)) timer->capability |= OMAP_TIMER_HAS_PWM; if (of_find_property(dev->of_node, "ti,timer-secure", NULL)) timer->capability |= OMAP_TIMER_SECURE; } else { timer->id = pdev->id; timer->capability = pdata->timer_capability; timer->reserved = omap_dm_timer_reserved_systimer(timer->id); } timer->omap1 = timer->capability & OMAP_TIMER_NEEDS_RESET; /* OMAP1 devices do not yet use the clock framework for dmtimers */ if (!timer->omap1) { timer->fclk = devm_clk_get(dev, "fck"); if (IS_ERR(timer->fclk)) return PTR_ERR(timer->fclk); } else { timer->fclk = ERR_PTR(-ENODEV); } if (!(timer->capability & OMAP_TIMER_ALWON)) { timer->nb.notifier_call = omap_timer_context_notifier; cpu_pm_register_notifier(&timer->nb); } timer->errata = pdata->timer_errata; timer->pdev = pdev; pm_runtime_enable(dev); if (!timer->reserved) { ret = pm_runtime_resume_and_get(dev); if (ret) { dev_err(dev, "%s: pm_runtime_get_sync failed!\n", __func__); goto err_disable; } __omap_dm_timer_init_regs(timer); /* Clear timer configuration */ dmtimer_write(timer, OMAP_TIMER_CTRL_REG, 0); pm_runtime_put(dev); } /* add the timer element to the list */ spin_lock_irqsave(&dm_timer_lock, flags); list_add_tail(&timer->node, &omap_timer_list); spin_unlock_irqrestore(&dm_timer_lock, flags); dev_dbg(dev, "Device Probed.\n"); return 0; err_disable: pm_runtime_disable(dev); return ret; } /** * omap_dm_timer_remove - cleanup a registered timer device * @pdev: pointer to current timer platform device * * Called by driver framework whenever a timer device is unregistered. * In addition to freeing platform resources it also deletes the timer * entry from the local list. */ static int omap_dm_timer_remove(struct platform_device *pdev) { struct dmtimer *timer; unsigned long flags; int ret = -EINVAL; spin_lock_irqsave(&dm_timer_lock, flags); list_for_each_entry(timer, &omap_timer_list, node) if (!strcmp(dev_name(&timer->pdev->dev), dev_name(&pdev->dev))) { if (!(timer->capability & OMAP_TIMER_ALWON)) cpu_pm_unregister_notifier(&timer->nb); list_del(&timer->node); ret = 0; break; } spin_unlock_irqrestore(&dm_timer_lock, flags); pm_runtime_disable(&pdev->dev); return ret; } static const struct omap_dm_timer_ops dmtimer_ops = { .request_by_node = omap_dm_timer_request_by_node, .request_specific = omap_dm_timer_request_specific, .request = omap_dm_timer_request, .set_source = omap_dm_timer_set_source, .get_irq = omap_dm_timer_get_irq, .set_int_enable = omap_dm_timer_set_int_enable, .set_int_disable = omap_dm_timer_set_int_disable, .free = omap_dm_timer_free, .enable = omap_dm_timer_enable, .disable = omap_dm_timer_disable, .get_fclk = omap_dm_timer_get_fclk, .start = omap_dm_timer_start, .stop = omap_dm_timer_stop, .set_load = omap_dm_timer_set_load, .set_match = omap_dm_timer_set_match, .set_pwm = omap_dm_timer_set_pwm, .get_pwm_status = omap_dm_timer_get_pwm_status, .set_prescaler = omap_dm_timer_set_prescaler, .read_counter = omap_dm_timer_read_counter, .write_counter = omap_dm_timer_write_counter, .read_status = omap_dm_timer_read_status, .write_status = omap_dm_timer_write_status, }; static const struct dmtimer_platform_data omap3plus_pdata = { .timer_errata = OMAP_TIMER_ERRATA_I103_I767, .timer_ops = &dmtimer_ops, }; static const struct dmtimer_platform_data am6_pdata = { .timer_ops = &dmtimer_ops, }; static const struct of_device_id omap_timer_match[] = { { .compatible = "ti,omap2420-timer", }, { .compatible = "ti,omap3430-timer", .data = &omap3plus_pdata, }, { .compatible = "ti,omap4430-timer", .data = &omap3plus_pdata, }, { .compatible = "ti,omap5430-timer", .data = &omap3plus_pdata, }, { .compatible = "ti,am335x-timer", .data = &omap3plus_pdata, }, { .compatible = "ti,am335x-timer-1ms", .data = &omap3plus_pdata, }, { .compatible = "ti,dm816-timer", .data = &omap3plus_pdata, }, { .compatible = "ti,am654-timer", .data = &am6_pdata, }, {}, }; MODULE_DEVICE_TABLE(of, omap_timer_match); static struct platform_driver omap_dm_timer_driver = { .probe = omap_dm_timer_probe, .remove = omap_dm_timer_remove, .driver = { .name = "omap_timer", .of_match_table = omap_timer_match, .pm = &omap_dm_timer_pm_ops, }, }; module_platform_driver(omap_dm_timer_driver); MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Texas Instruments Inc");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
You can’t perform that action at this time.