162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2012 John Crispin <john@phrozen.org>
562306a36Sopenharmony_ci *  Copyright (C) 2012 Lantiq GmbH
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/ioport.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1262306a36Sopenharmony_ci#include <linux/of_irq.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <lantiq_soc.h>
1662306a36Sopenharmony_ci#include "../clk.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* the magic ID byte of the core */
1962306a36Sopenharmony_ci#define GPTU_MAGIC	0x59
2062306a36Sopenharmony_ci/* clock control register */
2162306a36Sopenharmony_ci#define GPTU_CLC	0x00
2262306a36Sopenharmony_ci/* id register */
2362306a36Sopenharmony_ci#define GPTU_ID		0x08
2462306a36Sopenharmony_ci/* interrupt node enable */
2562306a36Sopenharmony_ci#define GPTU_IRNEN	0xf4
2662306a36Sopenharmony_ci/* interrupt control register */
2762306a36Sopenharmony_ci#define GPTU_IRCR	0xf8
2862306a36Sopenharmony_ci/* interrupt capture register */
2962306a36Sopenharmony_ci#define GPTU_IRNCR	0xfc
3062306a36Sopenharmony_ci/* there are 3 identical blocks of 2 timers. calculate register offsets */
3162306a36Sopenharmony_ci#define GPTU_SHIFT(x)	(x % 2 ? 4 : 0)
3262306a36Sopenharmony_ci#define GPTU_BASE(x)	(((x >> 1) * 0x20) + 0x10)
3362306a36Sopenharmony_ci/* timer control register */
3462306a36Sopenharmony_ci#define GPTU_CON(x)	(GPTU_BASE(x) + GPTU_SHIFT(x) + 0x00)
3562306a36Sopenharmony_ci/* timer auto reload register */
3662306a36Sopenharmony_ci#define GPTU_RUN(x)	(GPTU_BASE(x) + GPTU_SHIFT(x) + 0x08)
3762306a36Sopenharmony_ci/* timer manual reload register */
3862306a36Sopenharmony_ci#define GPTU_RLD(x)	(GPTU_BASE(x) + GPTU_SHIFT(x) + 0x10)
3962306a36Sopenharmony_ci/* timer count register */
4062306a36Sopenharmony_ci#define GPTU_CNT(x)	(GPTU_BASE(x) + GPTU_SHIFT(x) + 0x18)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* GPTU_CON(x) */
4362306a36Sopenharmony_ci#define CON_CNT		BIT(2)
4462306a36Sopenharmony_ci#define CON_EDGE_ANY	(BIT(7) | BIT(6))
4562306a36Sopenharmony_ci#define CON_SYNC	BIT(8)
4662306a36Sopenharmony_ci#define CON_CLK_INT	BIT(10)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* GPTU_RUN(x) */
4962306a36Sopenharmony_ci#define RUN_SEN		BIT(0)
5062306a36Sopenharmony_ci#define RUN_RL		BIT(2)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* set clock to runmode */
5362306a36Sopenharmony_ci#define CLC_RMC		BIT(8)
5462306a36Sopenharmony_ci/* bring core out of suspend */
5562306a36Sopenharmony_ci#define CLC_SUSPEND	BIT(4)
5662306a36Sopenharmony_ci/* the disable bit */
5762306a36Sopenharmony_ci#define CLC_DISABLE	BIT(0)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define gptu_w32(x, y)	ltq_w32((x), gptu_membase + (y))
6062306a36Sopenharmony_ci#define gptu_r32(x)	ltq_r32(gptu_membase + (x))
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cienum gptu_timer {
6362306a36Sopenharmony_ci	TIMER1A = 0,
6462306a36Sopenharmony_ci	TIMER1B,
6562306a36Sopenharmony_ci	TIMER2A,
6662306a36Sopenharmony_ci	TIMER2B,
6762306a36Sopenharmony_ci	TIMER3A,
6862306a36Sopenharmony_ci	TIMER3B
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void __iomem *gptu_membase;
7262306a36Sopenharmony_cistatic struct resource irqres[6];
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic irqreturn_t timer_irq_handler(int irq, void *priv)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int timer = irq - irqres[0].start;
7762306a36Sopenharmony_ci	gptu_w32(1 << timer, GPTU_IRNCR);
7862306a36Sopenharmony_ci	return IRQ_HANDLED;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void gptu_hwinit(void)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	gptu_w32(0x00, GPTU_IRNEN);
8462306a36Sopenharmony_ci	gptu_w32(0xff, GPTU_IRNCR);
8562306a36Sopenharmony_ci	gptu_w32(CLC_RMC | CLC_SUSPEND, GPTU_CLC);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void gptu_hwexit(void)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	gptu_w32(0x00, GPTU_IRNEN);
9162306a36Sopenharmony_ci	gptu_w32(0xff, GPTU_IRNCR);
9262306a36Sopenharmony_ci	gptu_w32(CLC_DISABLE, GPTU_CLC);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int gptu_enable(struct clk *clk)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	int ret = request_irq(irqres[clk->bits].start, timer_irq_handler,
9862306a36Sopenharmony_ci		IRQF_TIMER, "gtpu", NULL);
9962306a36Sopenharmony_ci	if (ret) {
10062306a36Sopenharmony_ci		pr_err("gptu: failed to request irq\n");
10162306a36Sopenharmony_ci		return ret;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	gptu_w32(CON_CNT | CON_EDGE_ANY | CON_SYNC | CON_CLK_INT,
10562306a36Sopenharmony_ci		GPTU_CON(clk->bits));
10662306a36Sopenharmony_ci	gptu_w32(1, GPTU_RLD(clk->bits));
10762306a36Sopenharmony_ci	gptu_w32(gptu_r32(GPTU_IRNEN) | BIT(clk->bits), GPTU_IRNEN);
10862306a36Sopenharmony_ci	gptu_w32(RUN_SEN | RUN_RL, GPTU_RUN(clk->bits));
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void gptu_disable(struct clk *clk)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	gptu_w32(0, GPTU_RUN(clk->bits));
11562306a36Sopenharmony_ci	gptu_w32(0, GPTU_CON(clk->bits));
11662306a36Sopenharmony_ci	gptu_w32(0, GPTU_RLD(clk->bits));
11762306a36Sopenharmony_ci	gptu_w32(gptu_r32(GPTU_IRNEN) & ~BIT(clk->bits), GPTU_IRNEN);
11862306a36Sopenharmony_ci	free_irq(irqres[clk->bits].start, NULL);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline void clkdev_add_gptu(struct device *dev, const char *con,
12262306a36Sopenharmony_ci							unsigned int timer)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!clk)
12762306a36Sopenharmony_ci		return;
12862306a36Sopenharmony_ci	clk->cl.dev_id = dev_name(dev);
12962306a36Sopenharmony_ci	clk->cl.con_id = con;
13062306a36Sopenharmony_ci	clk->cl.clk = clk;
13162306a36Sopenharmony_ci	clk->enable = gptu_enable;
13262306a36Sopenharmony_ci	clk->disable = gptu_disable;
13362306a36Sopenharmony_ci	clk->bits = timer;
13462306a36Sopenharmony_ci	clkdev_add(&clk->cl);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int gptu_probe(struct platform_device *pdev)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct clk *clk;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (of_irq_to_resource_table(pdev->dev.of_node, irqres, 6) != 6) {
14262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get IRQ list\n");
14362306a36Sopenharmony_ci		return -EINVAL;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* remap gptu register range */
14762306a36Sopenharmony_ci	gptu_membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
14862306a36Sopenharmony_ci	if (IS_ERR(gptu_membase))
14962306a36Sopenharmony_ci		return PTR_ERR(gptu_membase);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* enable our clock */
15262306a36Sopenharmony_ci	clk = clk_get(&pdev->dev, NULL);
15362306a36Sopenharmony_ci	if (IS_ERR(clk)) {
15462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get clock\n");
15562306a36Sopenharmony_ci		return -ENOENT;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci	clk_enable(clk);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* power up the core */
16062306a36Sopenharmony_ci	gptu_hwinit();
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* the gptu has a ID register */
16362306a36Sopenharmony_ci	if (((gptu_r32(GPTU_ID) >> 8) & 0xff) != GPTU_MAGIC) {
16462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to find magic\n");
16562306a36Sopenharmony_ci		gptu_hwexit();
16662306a36Sopenharmony_ci		clk_disable(clk);
16762306a36Sopenharmony_ci		clk_put(clk);
16862306a36Sopenharmony_ci		return -ENAVAIL;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* register the clocks */
17262306a36Sopenharmony_ci	clkdev_add_gptu(&pdev->dev, "timer1a", TIMER1A);
17362306a36Sopenharmony_ci	clkdev_add_gptu(&pdev->dev, "timer1b", TIMER1B);
17462306a36Sopenharmony_ci	clkdev_add_gptu(&pdev->dev, "timer2a", TIMER2A);
17562306a36Sopenharmony_ci	clkdev_add_gptu(&pdev->dev, "timer2b", TIMER2B);
17662306a36Sopenharmony_ci	clkdev_add_gptu(&pdev->dev, "timer3a", TIMER3A);
17762306a36Sopenharmony_ci	clkdev_add_gptu(&pdev->dev, "timer3b", TIMER3B);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	dev_info(&pdev->dev, "gptu: 6 timers loaded\n");
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic const struct of_device_id gptu_match[] = {
18562306a36Sopenharmony_ci	{ .compatible = "lantiq,gptu-xway" },
18662306a36Sopenharmony_ci	{},
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic struct platform_driver dma_driver = {
19062306a36Sopenharmony_ci	.probe = gptu_probe,
19162306a36Sopenharmony_ci	.driver = {
19262306a36Sopenharmony_ci		.name = "gptu-xway",
19362306a36Sopenharmony_ci		.of_match_table = gptu_match,
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ciint __init gptu_init(void)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	int ret = platform_driver_register(&dma_driver);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (ret)
20262306a36Sopenharmony_ci		pr_info("gptu: Error registering platform driver\n");
20362306a36Sopenharmony_ci	return ret;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciarch_initcall(gptu_init);
207