1639 lines · 39.7 KB · C
RawDownload
// 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/clocksource.h>#include <linux/clockchips.h>#include <linux/cpu_pm.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/device.h>#include <linux/err.h>#include <linux/pm_runtime.h>#include <linux/of.h>#include <linux/platform_device.h>#include <linux/platform_data/dmtimer-omap.h>#include <linux/sched_clock.h> #include <clocksource/timer-ti-dm.h>#include <linux/delay.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 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;	struct notifier_block fclk_nb;	unsigned long fclk_rate;}; static u32 omap_reserved_systimers;static LIST_HEAD(omap_timer_list);static DEFINE_SPINLOCK(dm_timer_lock); struct dmtimer_clocksource {	struct clocksource dev;	struct dmtimer *timer;	unsigned int loadval;}; struct omap_dm_timer_clockevent {	struct clock_event_device dev;	struct dmtimer *timer;	u32 period;}; static bool omap_dm_timer_clockevent_setup;static void __iomem *omap_dm_timer_sched_clock_counter; 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 * @val:        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){	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 / timer->fclk_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_timer_fclk_notifier(struct notifier_block *nb,				    unsigned long event, void *data){	struct clk_notifier_data *clk_data = data;	struct dmtimer *timer = container_of(nb, struct dmtimer, fclk_nb); 	switch (event) {	case POST_RATE_CHANGE:		timer->fclk_rate = clk_data->new_rate;		return NOTIFY_OK;	default:		return NOTIFY_DONE;	}} 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;

Lines 1–500 of 1639.