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