162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2011 Thomas Langer <thomas.langer@lantiq.com>
562306a36Sopenharmony_ci * Copyright (C) 2011 John Crispin <john@phrozen.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/ioport.h>
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <linux/clkdev.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci#include <asm/delay.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <lantiq_soc.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "../clk.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* infrastructure control register */
1962306a36Sopenharmony_ci#define SYS1_INFRAC		0x00bc
2062306a36Sopenharmony_ci/* Configuration fuses for drivers and pll */
2162306a36Sopenharmony_ci#define STATUS_CONFIG		0x0040
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* GPE frequency selection */
2462306a36Sopenharmony_ci#define GPPC_OFFSET		24
2562306a36Sopenharmony_ci#define GPEFREQ_MASK		0x0000C00
2662306a36Sopenharmony_ci#define GPEFREQ_OFFSET		10
2762306a36Sopenharmony_ci/* Clock status register */
2862306a36Sopenharmony_ci#define SYSCTL_CLKS		0x0000
2962306a36Sopenharmony_ci/* Clock enable register */
3062306a36Sopenharmony_ci#define SYSCTL_CLKEN		0x0004
3162306a36Sopenharmony_ci/* Clock clear register */
3262306a36Sopenharmony_ci#define SYSCTL_CLKCLR		0x0008
3362306a36Sopenharmony_ci/* Activation Status Register */
3462306a36Sopenharmony_ci#define SYSCTL_ACTS		0x0020
3562306a36Sopenharmony_ci/* Activation Register */
3662306a36Sopenharmony_ci#define SYSCTL_ACT		0x0024
3762306a36Sopenharmony_ci/* Deactivation Register */
3862306a36Sopenharmony_ci#define SYSCTL_DEACT		0x0028
3962306a36Sopenharmony_ci/* reboot Register */
4062306a36Sopenharmony_ci#define SYSCTL_RBT		0x002c
4162306a36Sopenharmony_ci/* CPU0 Clock Control Register */
4262306a36Sopenharmony_ci#define SYS1_CPU0CC		0x0040
4362306a36Sopenharmony_ci/* HRST_OUT_N Control Register */
4462306a36Sopenharmony_ci#define SYS1_HRSTOUTC		0x00c0
4562306a36Sopenharmony_ci/* clock divider bit */
4662306a36Sopenharmony_ci#define CPU0CC_CPUDIV		0x0001
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Activation Status Register */
4962306a36Sopenharmony_ci#define ACTS_ASC0_ACT	0x00001000
5062306a36Sopenharmony_ci#define ACTS_SSC0	0x00002000
5162306a36Sopenharmony_ci#define ACTS_ASC1_ACT	0x00000800
5262306a36Sopenharmony_ci#define ACTS_I2C_ACT	0x00004000
5362306a36Sopenharmony_ci#define ACTS_P0		0x00010000
5462306a36Sopenharmony_ci#define ACTS_P1		0x00010000
5562306a36Sopenharmony_ci#define ACTS_P2		0x00020000
5662306a36Sopenharmony_ci#define ACTS_P3		0x00020000
5762306a36Sopenharmony_ci#define ACTS_P4		0x00040000
5862306a36Sopenharmony_ci#define ACTS_PADCTRL0	0x00100000
5962306a36Sopenharmony_ci#define ACTS_PADCTRL1	0x00100000
6062306a36Sopenharmony_ci#define ACTS_PADCTRL2	0x00200000
6162306a36Sopenharmony_ci#define ACTS_PADCTRL3	0x00200000
6262306a36Sopenharmony_ci#define ACTS_PADCTRL4	0x00400000
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define sysctl_w32(m, x, y)	ltq_w32((x), sysctl_membase[m] + (y))
6562306a36Sopenharmony_ci#define sysctl_r32(m, x)	ltq_r32(sysctl_membase[m] + (x))
6662306a36Sopenharmony_ci#define sysctl_w32_mask(m, clear, set, reg)	\
6762306a36Sopenharmony_ci		sysctl_w32(m, (sysctl_r32(m, reg) & ~(clear)) | (set), reg)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define status_w32(x, y)	ltq_w32((x), status_membase + (y))
7062306a36Sopenharmony_ci#define status_r32(x)		ltq_r32(status_membase + (x))
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void __iomem *sysctl_membase[3], *status_membase;
7362306a36Sopenharmony_civoid __iomem *ltq_sys1_membase, *ltq_ebu_membase;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_civoid falcon_trigger_hrst(int level)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	sysctl_w32(SYSCTL_SYS1, level & 1, SYS1_HRSTOUTC);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline void sysctl_wait(struct clk *clk,
8162306a36Sopenharmony_ci		unsigned int test, unsigned int reg)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int err = 1000000;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	do {} while (--err && ((sysctl_r32(clk->module, reg)
8662306a36Sopenharmony_ci					& clk->bits) != test));
8762306a36Sopenharmony_ci	if (!err)
8862306a36Sopenharmony_ci		pr_err("module de/activation failed %d %08X %08X %08X\n",
8962306a36Sopenharmony_ci			clk->module, clk->bits, test,
9062306a36Sopenharmony_ci			sysctl_r32(clk->module, reg) & clk->bits);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int sysctl_activate(struct clk *clk)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_CLKEN);
9662306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_ACT);
9762306a36Sopenharmony_ci	sysctl_wait(clk, clk->bits, SYSCTL_ACTS);
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void sysctl_deactivate(struct clk *clk)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_CLKCLR);
10462306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_DEACT);
10562306a36Sopenharmony_ci	sysctl_wait(clk, 0, SYSCTL_ACTS);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int sysctl_clken(struct clk *clk)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_CLKEN);
11162306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_ACT);
11262306a36Sopenharmony_ci	sysctl_wait(clk, clk->bits, SYSCTL_CLKS);
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void sysctl_clkdis(struct clk *clk)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	sysctl_w32(clk->module, clk->bits, SYSCTL_CLKCLR);
11962306a36Sopenharmony_ci	sysctl_wait(clk, 0, SYSCTL_CLKS);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void sysctl_reboot(struct clk *clk)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned int act;
12562306a36Sopenharmony_ci	unsigned int bits;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	act = sysctl_r32(clk->module, SYSCTL_ACT);
12862306a36Sopenharmony_ci	bits = ~act & clk->bits;
12962306a36Sopenharmony_ci	if (bits != 0) {
13062306a36Sopenharmony_ci		sysctl_w32(clk->module, bits, SYSCTL_CLKEN);
13162306a36Sopenharmony_ci		sysctl_w32(clk->module, bits, SYSCTL_ACT);
13262306a36Sopenharmony_ci		sysctl_wait(clk, bits, SYSCTL_ACTS);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	sysctl_w32(clk->module, act & clk->bits, SYSCTL_RBT);
13562306a36Sopenharmony_ci	sysctl_wait(clk, clk->bits, SYSCTL_ACTS);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* enable the ONU core */
13962306a36Sopenharmony_cistatic void falcon_gpe_enable(void)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	unsigned int freq;
14262306a36Sopenharmony_ci	unsigned int status;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* if the clock is already enabled */
14562306a36Sopenharmony_ci	status = sysctl_r32(SYSCTL_SYS1, SYS1_INFRAC);
14662306a36Sopenharmony_ci	if (status & (1 << (GPPC_OFFSET + 1)))
14762306a36Sopenharmony_ci		return;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	freq = (status_r32(STATUS_CONFIG) &
15062306a36Sopenharmony_ci		GPEFREQ_MASK) >>
15162306a36Sopenharmony_ci		GPEFREQ_OFFSET;
15262306a36Sopenharmony_ci	if (freq == 0)
15362306a36Sopenharmony_ci		freq = 1; /* use 625MHz on unfused chip */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* apply new frequency */
15662306a36Sopenharmony_ci	sysctl_w32_mask(SYSCTL_SYS1, 7 << (GPPC_OFFSET + 1),
15762306a36Sopenharmony_ci		freq << (GPPC_OFFSET + 2) , SYS1_INFRAC);
15862306a36Sopenharmony_ci	udelay(1);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* enable new frequency */
16162306a36Sopenharmony_ci	sysctl_w32_mask(SYSCTL_SYS1, 0, 1 << (GPPC_OFFSET + 1), SYS1_INFRAC);
16262306a36Sopenharmony_ci	udelay(1);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic inline void clkdev_add_sys(const char *dev, unsigned int module,
16662306a36Sopenharmony_ci					unsigned int bits)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!clk)
17162306a36Sopenharmony_ci		return;
17262306a36Sopenharmony_ci	clk->cl.dev_id = dev;
17362306a36Sopenharmony_ci	clk->cl.con_id = NULL;
17462306a36Sopenharmony_ci	clk->cl.clk = clk;
17562306a36Sopenharmony_ci	clk->module = module;
17662306a36Sopenharmony_ci	clk->bits = bits;
17762306a36Sopenharmony_ci	clk->activate = sysctl_activate;
17862306a36Sopenharmony_ci	clk->deactivate = sysctl_deactivate;
17962306a36Sopenharmony_ci	clk->enable = sysctl_clken;
18062306a36Sopenharmony_ci	clk->disable = sysctl_clkdis;
18162306a36Sopenharmony_ci	clk->reboot = sysctl_reboot;
18262306a36Sopenharmony_ci	clkdev_add(&clk->cl);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_civoid __init ltq_soc_init(void)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct device_node *np_status =
18862306a36Sopenharmony_ci		of_find_compatible_node(NULL, NULL, "lantiq,status-falcon");
18962306a36Sopenharmony_ci	struct device_node *np_ebu =
19062306a36Sopenharmony_ci		of_find_compatible_node(NULL, NULL, "lantiq,ebu-falcon");
19162306a36Sopenharmony_ci	struct device_node *np_sys1 =
19262306a36Sopenharmony_ci		of_find_compatible_node(NULL, NULL, "lantiq,sys1-falcon");
19362306a36Sopenharmony_ci	struct device_node *np_syseth =
19462306a36Sopenharmony_ci		of_find_compatible_node(NULL, NULL, "lantiq,syseth-falcon");
19562306a36Sopenharmony_ci	struct device_node *np_sysgpe =
19662306a36Sopenharmony_ci		of_find_compatible_node(NULL, NULL, "lantiq,sysgpe-falcon");
19762306a36Sopenharmony_ci	struct resource res_status, res_ebu, res_sys[3];
19862306a36Sopenharmony_ci	int i;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* check if all the core register ranges are available */
20162306a36Sopenharmony_ci	if (!np_status || !np_ebu || !np_sys1 || !np_syseth || !np_sysgpe)
20262306a36Sopenharmony_ci		panic("Failed to load core nodes from devicetree");
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (of_address_to_resource(np_status, 0, &res_status) ||
20562306a36Sopenharmony_ci			of_address_to_resource(np_ebu, 0, &res_ebu) ||
20662306a36Sopenharmony_ci			of_address_to_resource(np_sys1, 0, &res_sys[0]) ||
20762306a36Sopenharmony_ci			of_address_to_resource(np_syseth, 0, &res_sys[1]) ||
20862306a36Sopenharmony_ci			of_address_to_resource(np_sysgpe, 0, &res_sys[2]))
20962306a36Sopenharmony_ci		panic("Failed to get core resources");
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	of_node_put(np_status);
21262306a36Sopenharmony_ci	of_node_put(np_ebu);
21362306a36Sopenharmony_ci	of_node_put(np_sys1);
21462306a36Sopenharmony_ci	of_node_put(np_syseth);
21562306a36Sopenharmony_ci	of_node_put(np_sysgpe);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if ((request_mem_region(res_status.start, resource_size(&res_status),
21862306a36Sopenharmony_ci				res_status.name) < 0) ||
21962306a36Sopenharmony_ci		(request_mem_region(res_ebu.start, resource_size(&res_ebu),
22062306a36Sopenharmony_ci				res_ebu.name) < 0) ||
22162306a36Sopenharmony_ci		(request_mem_region(res_sys[0].start,
22262306a36Sopenharmony_ci				resource_size(&res_sys[0]),
22362306a36Sopenharmony_ci				res_sys[0].name) < 0) ||
22462306a36Sopenharmony_ci		(request_mem_region(res_sys[1].start,
22562306a36Sopenharmony_ci				resource_size(&res_sys[1]),
22662306a36Sopenharmony_ci				res_sys[1].name) < 0) ||
22762306a36Sopenharmony_ci		(request_mem_region(res_sys[2].start,
22862306a36Sopenharmony_ci				resource_size(&res_sys[2]),
22962306a36Sopenharmony_ci				res_sys[2].name) < 0))
23062306a36Sopenharmony_ci		pr_err("Failed to request core resources");
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	status_membase = ioremap(res_status.start,
23362306a36Sopenharmony_ci					resource_size(&res_status));
23462306a36Sopenharmony_ci	ltq_ebu_membase = ioremap(res_ebu.start,
23562306a36Sopenharmony_ci					resource_size(&res_ebu));
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!status_membase || !ltq_ebu_membase)
23862306a36Sopenharmony_ci		panic("Failed to remap core resources");
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
24162306a36Sopenharmony_ci		sysctl_membase[i] = ioremap(res_sys[i].start,
24262306a36Sopenharmony_ci						resource_size(&res_sys[i]));
24362306a36Sopenharmony_ci		if (!sysctl_membase[i])
24462306a36Sopenharmony_ci			panic("Failed to remap sysctrl resources");
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci	ltq_sys1_membase = sysctl_membase[0];
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	falcon_gpe_enable();
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* get our 3 static rates for cpu, fpi and io clocks */
25162306a36Sopenharmony_ci	if (ltq_sys1_r32(SYS1_CPU0CC) & CPU0CC_CPUDIV)
25262306a36Sopenharmony_ci		clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M, 0);
25362306a36Sopenharmony_ci	else
25462306a36Sopenharmony_ci		clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M, 0);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* add our clock domains */
25762306a36Sopenharmony_ci	clkdev_add_sys("1d810000.gpio", SYSCTL_SYSETH, ACTS_P0);
25862306a36Sopenharmony_ci	clkdev_add_sys("1d810100.gpio", SYSCTL_SYSETH, ACTS_P2);
25962306a36Sopenharmony_ci	clkdev_add_sys("1e800100.gpio", SYSCTL_SYS1, ACTS_P1);
26062306a36Sopenharmony_ci	clkdev_add_sys("1e800200.gpio", SYSCTL_SYS1, ACTS_P3);
26162306a36Sopenharmony_ci	clkdev_add_sys("1e800300.gpio", SYSCTL_SYS1, ACTS_P4);
26262306a36Sopenharmony_ci	clkdev_add_sys("1db01000.pad", SYSCTL_SYSETH, ACTS_PADCTRL0);
26362306a36Sopenharmony_ci	clkdev_add_sys("1db02000.pad", SYSCTL_SYSETH, ACTS_PADCTRL2);
26462306a36Sopenharmony_ci	clkdev_add_sys("1e800400.pad", SYSCTL_SYS1, ACTS_PADCTRL1);
26562306a36Sopenharmony_ci	clkdev_add_sys("1e800500.pad", SYSCTL_SYS1, ACTS_PADCTRL3);
26662306a36Sopenharmony_ci	clkdev_add_sys("1e800600.pad", SYSCTL_SYS1, ACTS_PADCTRL4);
26762306a36Sopenharmony_ci	clkdev_add_sys("1e100b00.serial", SYSCTL_SYS1, ACTS_ASC1_ACT);
26862306a36Sopenharmony_ci	clkdev_add_sys("1e100c00.serial", SYSCTL_SYS1, ACTS_ASC0_ACT);
26962306a36Sopenharmony_ci	clkdev_add_sys("1e100d00.spi", SYSCTL_SYS1, ACTS_SSC0);
27062306a36Sopenharmony_ci	clkdev_add_sys("1e200000.i2c", SYSCTL_SYS1, ACTS_I2C_ACT);
27162306a36Sopenharmony_ci}
272