440 lines · 10.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  ARM Generic Memory Mapped Timer support * *  Split from drivers/clocksource/arm_arch_timer.c * *  Copyright (C) 2011 ARM Ltd. *  All Rights Reserved */ #define pr_fmt(fmt) 	"arch_timer_mmio: " fmt #include <linux/clockchips.h>#include <linux/interrupt.h>#include <linux/io-64-nonatomic-lo-hi.h>#include <linux/of_irq.h>#include <linux/of_address.h>#include <linux/platform_device.h> #include <clocksource/arm_arch_timer.h> #define CNTTIDR		0x08#define CNTTIDR_VIRT(n)	(BIT(1) << ((n) * 4)) #define CNTACR(n)	(0x40 + ((n) * 4))#define CNTACR_RPCT	BIT(0)#define CNTACR_RVCT	BIT(1)#define CNTACR_RFRQ	BIT(2)#define CNTACR_RVOFF	BIT(3)#define CNTACR_RWVT	BIT(4)#define CNTACR_RWPT	BIT(5) #define CNTPCT_LO	0x00#define CNTVCT_LO	0x08#define CNTFRQ		0x10#define CNTP_CVAL_LO	0x20#define CNTP_CTL	0x2c#define CNTV_CVAL_LO	0x30#define CNTV_CTL	0x3c enum arch_timer_access {	PHYS_ACCESS,	VIRT_ACCESS,}; struct arch_timer {	struct clock_event_device	evt;	struct clocksource		cs;	struct arch_timer_mem		*gt_block;	void __iomem			*base;	enum arch_timer_access		access;	u32				rate;}; #define evt_to_arch_timer(e) container_of(e, struct arch_timer, evt)#define cs_to_arch_timer(c) container_of(c, struct arch_timer, cs) static void arch_timer_mmio_write(struct arch_timer *timer,				  enum arch_timer_reg reg, u64 val){	switch (timer->access) {	case PHYS_ACCESS:		switch (reg) {		case ARCH_TIMER_REG_CTRL:			writel_relaxed((u32)val, timer->base + CNTP_CTL);			return;		case ARCH_TIMER_REG_CVAL:			/*			 * Not guaranteed to be atomic, so the timer			 * must be disabled at this point.			 */			writeq_relaxed(val, timer->base + CNTP_CVAL_LO);			return;		}		break;	case VIRT_ACCESS:		switch (reg) {		case ARCH_TIMER_REG_CTRL:			writel_relaxed((u32)val, timer->base + CNTV_CTL);			return;		case ARCH_TIMER_REG_CVAL:			/* Same restriction as above */			writeq_relaxed(val, timer->base + CNTV_CVAL_LO);			return;		}		break;	} 	/* Should never be here */	WARN_ON_ONCE(1);} static u32 arch_timer_mmio_read(struct arch_timer *timer, enum arch_timer_reg reg){	switch (timer->access) {	case PHYS_ACCESS:		switch (reg) {		case ARCH_TIMER_REG_CTRL:			return readl_relaxed(timer->base + CNTP_CTL);		default:			break;		}		break;	case VIRT_ACCESS:		switch (reg) {		case ARCH_TIMER_REG_CTRL:			return readl_relaxed(timer->base + CNTV_CTL);		default:			break;		}		break;	} 	/* Should never be here */	WARN_ON_ONCE(1);	return 0;} static noinstr u64 arch_counter_mmio_get_cnt(struct arch_timer *t){	int offset_lo = t->access == VIRT_ACCESS ? CNTVCT_LO : CNTPCT_LO;	u32 cnt_lo, cnt_hi, tmp_hi; 	do {		cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4));		cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo));		tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4));	} while (cnt_hi != tmp_hi); 	return ((u64) cnt_hi << 32) | cnt_lo;} static u64 arch_mmio_counter_read(struct clocksource *cs){	struct arch_timer *at = cs_to_arch_timer(cs); 	return arch_counter_mmio_get_cnt(at);} static int arch_timer_mmio_shutdown(struct clock_event_device *clk){	struct arch_timer *at = evt_to_arch_timer(clk);	unsigned long ctrl; 	ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL);	ctrl &= ~ARCH_TIMER_CTRL_ENABLE;	arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl); 	return 0;} static int arch_timer_mmio_set_next_event(unsigned long evt,					  struct clock_event_device *clk){	struct arch_timer *timer = evt_to_arch_timer(clk);	unsigned long ctrl;	u64 cnt; 	ctrl = arch_timer_mmio_read(timer, ARCH_TIMER_REG_CTRL); 	/* Timer must be disabled before programming CVAL */	if (ctrl & ARCH_TIMER_CTRL_ENABLE) {		ctrl &= ~ARCH_TIMER_CTRL_ENABLE;		arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl);	} 	ctrl |= ARCH_TIMER_CTRL_ENABLE;	ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; 	cnt = arch_counter_mmio_get_cnt(timer); 	arch_timer_mmio_write(timer, ARCH_TIMER_REG_CVAL, evt + cnt);	arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl);	return 0;} static irqreturn_t arch_timer_mmio_handler(int irq, void *dev_id){	struct clock_event_device *evt = dev_id;	struct arch_timer *at = evt_to_arch_timer(evt);	unsigned long ctrl; 	ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL);	if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {		ctrl |= ARCH_TIMER_CTRL_IT_MASK;		arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl);		evt->event_handler(evt);		return IRQ_HANDLED;	} 	return IRQ_NONE;} static struct arch_timer_mem_frame *find_best_frame(struct platform_device *pdev){	struct arch_timer_mem_frame *frame, *best_frame = NULL;	struct arch_timer *at = platform_get_drvdata(pdev);	void __iomem *cntctlbase;	u32 cnttidr; 	cntctlbase = ioremap(at->gt_block->cntctlbase, at->gt_block->size);	if (!cntctlbase) {		dev_err(&pdev->dev, "Can't map CNTCTLBase @ %pa\n",			&at->gt_block->cntctlbase);		return NULL;	} 	cnttidr = readl_relaxed(cntctlbase + CNTTIDR); 	/*	 * Try to find a virtual capable frame. Otherwise fall back to a	 * physical capable frame.	 */	for (int i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) {		u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT |			     CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT; 		frame = &at->gt_block->frame[i];		if (!frame->valid)			continue; 		/* Try enabling everything, and see what sticks */		writel_relaxed(cntacr, cntctlbase + CNTACR(i));		cntacr = readl_relaxed(cntctlbase + CNTACR(i)); 		/* Pick a suitable frame for which we have an IRQ */		if ((cnttidr & CNTTIDR_VIRT(i)) &&		    !(~cntacr & (CNTACR_RWVT | CNTACR_RVCT)) &&		    frame->virt_irq) {			best_frame = frame;			at->access = VIRT_ACCESS;			break;		} 		if ((~cntacr & (CNTACR_RWPT | CNTACR_RPCT)) ||		     !frame->phys_irq)			continue; 		at->access = PHYS_ACCESS;		best_frame = frame;	} 	iounmap(cntctlbase); 	return best_frame;} static void arch_timer_mmio_setup(struct arch_timer *at, int irq){	at->evt = (struct clock_event_device) {		.features		   = (CLOCK_EVT_FEAT_ONESHOT |					      CLOCK_EVT_FEAT_DYNIRQ),		.name			   = "arch_mem_timer",		.rating			   = 400,		.cpumask		   = cpu_possible_mask,		.irq 			   = irq,		.set_next_event		   = arch_timer_mmio_set_next_event,		.set_state_oneshot_stopped = arch_timer_mmio_shutdown,		.set_state_shutdown	   = arch_timer_mmio_shutdown,	}; 	at->evt.set_state_shutdown(&at->evt); 	clockevents_config_and_register(&at->evt, at->rate, 0xf,					(unsigned long)CLOCKSOURCE_MASK(56)); 	enable_irq(at->evt.irq); 	at->cs = (struct clocksource) {		.name	= "arch_mmio_counter",		.rating	= 300,		.read	= arch_mmio_counter_read,		.mask	= CLOCKSOURCE_MASK(56),		.flags	= CLOCK_SOURCE_IS_CONTINUOUS,	}; 	clocksource_register_hz(&at->cs, at->rate);} static int arch_timer_mmio_frame_register(struct platform_device *pdev,					  struct arch_timer_mem_frame *frame){	struct arch_timer *at = platform_get_drvdata(pdev);	struct device_node *np = pdev->dev.of_node;	int ret, irq;	u32 rate; 	if (!devm_request_mem_region(&pdev->dev, frame->cntbase, frame->size,				     "arch_mem_timer"))		return -EBUSY; 	at->base = devm_ioremap(&pdev->dev, frame->cntbase, frame->size);	if (!at->base) {		dev_err(&pdev->dev, "Can't map frame's registers\n");		return -ENXIO;	} 	/*	 * Allow "clock-frequency" to override the probed rate. If neither	 * lead to something useful, use the CPU timer frequency as the	 * fallback. The nice thing about that last point is that we woudn't	 * made it here if we didn't have a valid frequency.	 */	rate = readl_relaxed(at->base + CNTFRQ); 	if (!np || of_property_read_u32(np, "clock-frequency", &at->rate))		at->rate = rate; 	if (!at->rate)		at->rate = arch_timer_get_rate(); 	irq = at->access == VIRT_ACCESS ? frame->virt_irq : frame->phys_irq;	ret = devm_request_irq(&pdev->dev, irq, arch_timer_mmio_handler,			       IRQF_TIMER | IRQF_NO_AUTOEN, "arch_mem_timer",			       &at->evt);	if (ret)		return ret; 	/* Afer this point, we're not allowed to fail anymore */	arch_timer_mmio_setup(at, irq);	return 0;} static int of_populate_gt_block(struct platform_device *pdev,				struct arch_timer *at){	struct resource res; 	if (of_address_to_resource(pdev->dev.of_node, 0, &res))		return -EINVAL; 	at->gt_block->cntctlbase = res.start;	at->gt_block->size = resource_size(&res); 	for_each_available_child_of_node_scoped(pdev->dev.of_node, frame_node) {		struct arch_timer_mem_frame *frame;		u32 n; 		if (of_property_read_u32(frame_node, "frame-number", &n)) {			dev_err(&pdev->dev, FW_BUG "Missing frame-number\n");			return -EINVAL;		}		if (n >= ARCH_TIMER_MEM_MAX_FRAMES) {			dev_err(&pdev->dev,				FW_BUG "Wrong frame-number, only 0-%u are permitted\n",			       ARCH_TIMER_MEM_MAX_FRAMES - 1);			return -EINVAL;		} 		frame = &at->gt_block->frame[n]; 		if (frame->valid) {			dev_err(&pdev->dev, FW_BUG "Duplicated frame-number\n");			return -EINVAL;		} 		if (of_address_to_resource(frame_node, 0, &res))			return -EINVAL; 		frame->cntbase = res.start;		frame->size = resource_size(&res); 		frame->phys_irq = irq_of_parse_and_map(frame_node, 0);		frame->virt_irq = irq_of_parse_and_map(frame_node, 1); 		frame->valid = true;	} 	return 0;} static int arch_timer_mmio_probe(struct platform_device *pdev){	struct arch_timer_mem_frame *frame;	struct arch_timer *at;	struct device_node *np;	int ret; 	np = pdev->dev.of_node; 	at = devm_kmalloc(&pdev->dev, sizeof(*at), GFP_KERNEL | __GFP_ZERO);	if (!at)		return -ENOMEM; 	if (np) {		at->gt_block = devm_kmalloc(&pdev->dev, sizeof(*at->gt_block),					    GFP_KERNEL | __GFP_ZERO);		if (!at->gt_block)			return -ENOMEM;		ret = of_populate_gt_block(pdev, at);		if (ret)			return ret;	} else {		at->gt_block = dev_get_platdata(&pdev->dev);	} 	platform_set_drvdata(pdev, at); 	frame = find_best_frame(pdev);	if (!frame) {		dev_err(&pdev->dev,			"Unable to find a suitable frame in timer @ %pa\n",			&at->gt_block->cntctlbase);		return -EINVAL;	} 	ret = arch_timer_mmio_frame_register(pdev, frame);	if (!ret)		dev_info(&pdev->dev,			 "mmio timer running at %lu.%02luMHz (%s)\n",			 (unsigned long)at->rate / 1000000,			 (unsigned long)(at->rate / 10000) % 100,			 at->access == VIRT_ACCESS ? "virt" : "phys"); 	return ret;} static const struct of_device_id arch_timer_mmio_of_table[] = {	{ .compatible = "arm,armv7-timer-mem", },	{}}; static struct platform_driver arch_timer_mmio_drv = {	.driver	= {		.name = "arch-timer-mmio",		.of_match_table	= arch_timer_mmio_of_table,		.suppress_bind_attrs = true,	},	.probe	= arch_timer_mmio_probe,};builtin_platform_driver(arch_timer_mmio_drv); static struct platform_driver arch_timer_mmio_acpi_drv = {	.driver	= {		.name = "gtdt-arm-mmio-timer",		.suppress_bind_attrs = true,	},	.probe	= arch_timer_mmio_probe,};builtin_platform_driver(arch_timer_mmio_acpi_drv);