xref: /linux/drivers/clk/clk-nspire.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27d1818faSDaniel Tang /*
37d1818faSDaniel Tang  *
47d1818faSDaniel Tang  *  Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
57d1818faSDaniel Tang  */
67d1818faSDaniel Tang 
77d1818faSDaniel Tang #include <linux/clk-provider.h>
87d1818faSDaniel Tang #include <linux/err.h>
97d1818faSDaniel Tang #include <linux/io.h>
107d1818faSDaniel Tang #include <linux/of.h>
117d1818faSDaniel Tang #include <linux/of_address.h>
127d1818faSDaniel Tang 
137d1818faSDaniel Tang #define MHZ (1000 * 1000)
147d1818faSDaniel Tang 
157d1818faSDaniel Tang #define BASE_CPU_SHIFT		1
167d1818faSDaniel Tang #define BASE_CPU_MASK		0x7F
177d1818faSDaniel Tang 
187d1818faSDaniel Tang #define CPU_AHB_SHIFT		12
197d1818faSDaniel Tang #define CPU_AHB_MASK		0x07
207d1818faSDaniel Tang 
217d1818faSDaniel Tang #define FIXED_BASE_SHIFT	8
227d1818faSDaniel Tang #define FIXED_BASE_MASK		0x01
237d1818faSDaniel Tang 
247d1818faSDaniel Tang #define CLASSIC_BASE_SHIFT	16
257d1818faSDaniel Tang #define CLASSIC_BASE_MASK	0x1F
267d1818faSDaniel Tang 
277d1818faSDaniel Tang #define CX_BASE_SHIFT		15
287d1818faSDaniel Tang #define CX_BASE_MASK		0x3F
297d1818faSDaniel Tang 
307d1818faSDaniel Tang #define CX_UNKNOWN_SHIFT	21
317d1818faSDaniel Tang #define CX_UNKNOWN_MASK		0x03
327d1818faSDaniel Tang 
337d1818faSDaniel Tang struct nspire_clk_info {
347d1818faSDaniel Tang 	u32 base_clock;
357d1818faSDaniel Tang 	u16 base_cpu_ratio;
367d1818faSDaniel Tang 	u16 base_ahb_ratio;
377d1818faSDaniel Tang };
387d1818faSDaniel Tang 
397d1818faSDaniel Tang 
407d1818faSDaniel Tang #define EXTRACT(var, prop) (((var)>>prop##_SHIFT) & prop##_MASK)
417d1818faSDaniel Tang static void nspire_clkinfo_cx(u32 val, struct nspire_clk_info *clk)
427d1818faSDaniel Tang {
437d1818faSDaniel Tang 	if (EXTRACT(val, FIXED_BASE))
447d1818faSDaniel Tang 		clk->base_clock = 48 * MHZ;
457d1818faSDaniel Tang 	else
467d1818faSDaniel Tang 		clk->base_clock = 6 * EXTRACT(val, CX_BASE) * MHZ;
477d1818faSDaniel Tang 
487d1818faSDaniel Tang 	clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * EXTRACT(val, CX_UNKNOWN);
497d1818faSDaniel Tang 	clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1);
507d1818faSDaniel Tang }
517d1818faSDaniel Tang 
527d1818faSDaniel Tang static void nspire_clkinfo_classic(u32 val, struct nspire_clk_info *clk)
537d1818faSDaniel Tang {
547d1818faSDaniel Tang 	if (EXTRACT(val, FIXED_BASE))
557d1818faSDaniel Tang 		clk->base_clock = 27 * MHZ;
567d1818faSDaniel Tang 	else
577d1818faSDaniel Tang 		clk->base_clock = (300 - 6 * EXTRACT(val, CLASSIC_BASE)) * MHZ;
587d1818faSDaniel Tang 
597d1818faSDaniel Tang 	clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * 2;
607d1818faSDaniel Tang 	clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1);
617d1818faSDaniel Tang }
627d1818faSDaniel Tang 
637d1818faSDaniel Tang static void __init nspire_ahbdiv_setup(struct device_node *node,
647d1818faSDaniel Tang 		void (*get_clkinfo)(u32, struct nspire_clk_info *))
657d1818faSDaniel Tang {
667d1818faSDaniel Tang 	u32 val;
677d1818faSDaniel Tang 	void __iomem *io;
68416886adSStephen Boyd 	struct clk_hw *hw;
697d1818faSDaniel Tang 	const char *clk_name = node->name;
707d1818faSDaniel Tang 	const char *parent_name;
717d1818faSDaniel Tang 	struct nspire_clk_info info;
727d1818faSDaniel Tang 
737d1818faSDaniel Tang 	io = of_iomap(node, 0);
747d1818faSDaniel Tang 	if (!io)
757d1818faSDaniel Tang 		return;
767d1818faSDaniel Tang 	val = readl(io);
777d1818faSDaniel Tang 	iounmap(io);
787d1818faSDaniel Tang 
797d1818faSDaniel Tang 	get_clkinfo(val, &info);
807d1818faSDaniel Tang 
817d1818faSDaniel Tang 	of_property_read_string(node, "clock-output-names", &clk_name);
827d1818faSDaniel Tang 	parent_name = of_clk_get_parent_name(node, 0);
837d1818faSDaniel Tang 
84416886adSStephen Boyd 	hw = clk_hw_register_fixed_factor(NULL, clk_name, parent_name, 0,
857d1818faSDaniel Tang 					  1, info.base_ahb_ratio);
86416886adSStephen Boyd 	if (!IS_ERR(hw))
87416886adSStephen Boyd 		of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
887d1818faSDaniel Tang }
897d1818faSDaniel Tang 
907d1818faSDaniel Tang static void __init nspire_ahbdiv_setup_cx(struct device_node *node)
917d1818faSDaniel Tang {
927d1818faSDaniel Tang 	nspire_ahbdiv_setup(node, nspire_clkinfo_cx);
937d1818faSDaniel Tang }
947d1818faSDaniel Tang 
957d1818faSDaniel Tang static void __init nspire_ahbdiv_setup_classic(struct device_node *node)
967d1818faSDaniel Tang {
977d1818faSDaniel Tang 	nspire_ahbdiv_setup(node, nspire_clkinfo_classic);
987d1818faSDaniel Tang }
997d1818faSDaniel Tang 
1007d1818faSDaniel Tang CLK_OF_DECLARE(nspire_ahbdiv_cx, "lsi,nspire-cx-ahb-divider",
1017d1818faSDaniel Tang 		nspire_ahbdiv_setup_cx);
1027d1818faSDaniel Tang CLK_OF_DECLARE(nspire_ahbdiv_classic, "lsi,nspire-classic-ahb-divider",
1037d1818faSDaniel Tang 		nspire_ahbdiv_setup_classic);
1047d1818faSDaniel Tang 
1057d1818faSDaniel Tang static void __init nspire_clk_setup(struct device_node *node,
1067d1818faSDaniel Tang 		void (*get_clkinfo)(u32, struct nspire_clk_info *))
1077d1818faSDaniel Tang {
1087d1818faSDaniel Tang 	u32 val;
1097d1818faSDaniel Tang 	void __iomem *io;
110416886adSStephen Boyd 	struct clk_hw *hw;
1117d1818faSDaniel Tang 	const char *clk_name = node->name;
1127d1818faSDaniel Tang 	struct nspire_clk_info info;
1137d1818faSDaniel Tang 
1147d1818faSDaniel Tang 	io = of_iomap(node, 0);
1157d1818faSDaniel Tang 	if (!io)
1167d1818faSDaniel Tang 		return;
1177d1818faSDaniel Tang 	val = readl(io);
1187d1818faSDaniel Tang 	iounmap(io);
1197d1818faSDaniel Tang 
1207d1818faSDaniel Tang 	get_clkinfo(val, &info);
1217d1818faSDaniel Tang 
1227d1818faSDaniel Tang 	of_property_read_string(node, "clock-output-names", &clk_name);
1237d1818faSDaniel Tang 
124416886adSStephen Boyd 	hw = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0,
125416886adSStephen Boyd 					info.base_clock);
126416886adSStephen Boyd 	if (!IS_ERR(hw))
127416886adSStephen Boyd 		of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
1287d1818faSDaniel Tang 	else
1297d1818faSDaniel Tang 		return;
1307d1818faSDaniel Tang 
1317d1818faSDaniel Tang 	pr_info("TI-NSPIRE Base: %uMHz CPU: %uMHz AHB: %uMHz\n",
1327d1818faSDaniel Tang 		info.base_clock / MHZ,
1337d1818faSDaniel Tang 		info.base_clock / info.base_cpu_ratio / MHZ,
1347d1818faSDaniel Tang 		info.base_clock / info.base_ahb_ratio / MHZ);
1357d1818faSDaniel Tang }
1367d1818faSDaniel Tang 
1377d1818faSDaniel Tang static void __init nspire_clk_setup_cx(struct device_node *node)
1387d1818faSDaniel Tang {
1397d1818faSDaniel Tang 	nspire_clk_setup(node, nspire_clkinfo_cx);
1407d1818faSDaniel Tang }
1417d1818faSDaniel Tang 
1427d1818faSDaniel Tang static void __init nspire_clk_setup_classic(struct device_node *node)
1437d1818faSDaniel Tang {
1447d1818faSDaniel Tang 	nspire_clk_setup(node, nspire_clkinfo_classic);
1457d1818faSDaniel Tang }
1467d1818faSDaniel Tang 
1477d1818faSDaniel Tang CLK_OF_DECLARE(nspire_clk_cx, "lsi,nspire-cx-clock", nspire_clk_setup_cx);
1487d1818faSDaniel Tang CLK_OF_DECLARE(nspire_clk_classic, "lsi,nspire-classic-clock",
1497d1818faSDaniel Tang 		nspire_clk_setup_classic);
150