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)
nspire_clkinfo_cx(u32 val,struct nspire_clk_info * clk)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
nspire_clkinfo_classic(u32 val,struct nspire_clk_info * clk)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
nspire_ahbdiv_setup(struct device_node * node,void (* get_clkinfo)(u32,struct nspire_clk_info *))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
nspire_ahbdiv_setup_cx(struct device_node * node)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
nspire_ahbdiv_setup_classic(struct device_node * node)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
nspire_clk_setup(struct device_node * node,void (* get_clkinfo)(u32,struct nspire_clk_info *))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
nspire_clk_setup_cx(struct device_node * node)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
nspire_clk_setup_classic(struct device_node * node)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