1*6b0fd6c1SPaul Burton /* 2*6b0fd6c1SPaul Burton * Copyright (C) 2016-2017 Imagination Technologies 3*6b0fd6c1SPaul Burton * Author: Paul Burton <paul.burton@imgtec.com> 4*6b0fd6c1SPaul Burton * 5*6b0fd6c1SPaul Burton * This program is free software; you can redistribute it and/or modify it 6*6b0fd6c1SPaul Burton * under the terms of the GNU General Public License as published by the 7*6b0fd6c1SPaul Burton * Free Software Foundation; either version 2 of the License, or (at your 8*6b0fd6c1SPaul Burton * option) any later version. 9*6b0fd6c1SPaul Burton */ 10*6b0fd6c1SPaul Burton 11*6b0fd6c1SPaul Burton #define pr_fmt(fmt) "clk-boston: " fmt 12*6b0fd6c1SPaul Burton 13*6b0fd6c1SPaul Burton #include <linux/clk-provider.h> 14*6b0fd6c1SPaul Burton #include <linux/kernel.h> 15*6b0fd6c1SPaul Burton #include <linux/of.h> 16*6b0fd6c1SPaul Burton #include <linux/regmap.h> 17*6b0fd6c1SPaul Burton #include <linux/slab.h> 18*6b0fd6c1SPaul Burton #include <linux/mfd/syscon.h> 19*6b0fd6c1SPaul Burton 20*6b0fd6c1SPaul Burton #include <dt-bindings/clock/boston-clock.h> 21*6b0fd6c1SPaul Burton 22*6b0fd6c1SPaul Burton #define BOSTON_PLAT_MMCMDIV 0x30 23*6b0fd6c1SPaul Burton # define BOSTON_PLAT_MMCMDIV_CLK0DIV (0xff << 0) 24*6b0fd6c1SPaul Burton # define BOSTON_PLAT_MMCMDIV_INPUT (0xff << 8) 25*6b0fd6c1SPaul Burton # define BOSTON_PLAT_MMCMDIV_MUL (0xff << 16) 26*6b0fd6c1SPaul Burton # define BOSTON_PLAT_MMCMDIV_CLK1DIV (0xff << 24) 27*6b0fd6c1SPaul Burton 28*6b0fd6c1SPaul Burton #define BOSTON_CLK_COUNT 3 29*6b0fd6c1SPaul Burton 30*6b0fd6c1SPaul Burton static u32 ext_field(u32 val, u32 mask) 31*6b0fd6c1SPaul Burton { 32*6b0fd6c1SPaul Burton return (val & mask) >> (ffs(mask) - 1); 33*6b0fd6c1SPaul Burton } 34*6b0fd6c1SPaul Burton 35*6b0fd6c1SPaul Burton static void __init clk_boston_setup(struct device_node *np) 36*6b0fd6c1SPaul Burton { 37*6b0fd6c1SPaul Burton unsigned long in_freq, cpu_freq, sys_freq; 38*6b0fd6c1SPaul Burton uint mmcmdiv, mul, cpu_div, sys_div; 39*6b0fd6c1SPaul Burton struct clk_hw_onecell_data *onecell; 40*6b0fd6c1SPaul Burton struct regmap *regmap; 41*6b0fd6c1SPaul Burton struct clk_hw *hw; 42*6b0fd6c1SPaul Burton int err; 43*6b0fd6c1SPaul Burton 44*6b0fd6c1SPaul Burton regmap = syscon_node_to_regmap(np->parent); 45*6b0fd6c1SPaul Burton if (IS_ERR(regmap)) { 46*6b0fd6c1SPaul Burton pr_err("failed to find regmap\n"); 47*6b0fd6c1SPaul Burton return; 48*6b0fd6c1SPaul Burton } 49*6b0fd6c1SPaul Burton 50*6b0fd6c1SPaul Burton err = regmap_read(regmap, BOSTON_PLAT_MMCMDIV, &mmcmdiv); 51*6b0fd6c1SPaul Burton if (err) { 52*6b0fd6c1SPaul Burton pr_err("failed to read mmcm_div register: %d\n", err); 53*6b0fd6c1SPaul Burton return; 54*6b0fd6c1SPaul Burton } 55*6b0fd6c1SPaul Burton 56*6b0fd6c1SPaul Burton in_freq = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_INPUT) * 1000000; 57*6b0fd6c1SPaul Burton mul = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_MUL); 58*6b0fd6c1SPaul Burton 59*6b0fd6c1SPaul Burton sys_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK0DIV); 60*6b0fd6c1SPaul Burton sys_freq = mult_frac(in_freq, mul, sys_div); 61*6b0fd6c1SPaul Burton 62*6b0fd6c1SPaul Burton cpu_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV); 63*6b0fd6c1SPaul Burton cpu_freq = mult_frac(in_freq, mul, cpu_div); 64*6b0fd6c1SPaul Burton 65*6b0fd6c1SPaul Burton onecell = kzalloc(sizeof(*onecell) + 66*6b0fd6c1SPaul Burton (BOSTON_CLK_COUNT * sizeof(struct clk_hw *)), 67*6b0fd6c1SPaul Burton GFP_KERNEL); 68*6b0fd6c1SPaul Burton if (!onecell) 69*6b0fd6c1SPaul Burton return; 70*6b0fd6c1SPaul Burton 71*6b0fd6c1SPaul Burton onecell->num = BOSTON_CLK_COUNT; 72*6b0fd6c1SPaul Burton 73*6b0fd6c1SPaul Burton hw = clk_hw_register_fixed_rate(NULL, "input", NULL, 0, in_freq); 74*6b0fd6c1SPaul Burton if (IS_ERR(hw)) { 75*6b0fd6c1SPaul Burton pr_err("failed to register input clock: %ld\n", PTR_ERR(hw)); 76*6b0fd6c1SPaul Burton return; 77*6b0fd6c1SPaul Burton } 78*6b0fd6c1SPaul Burton onecell->hws[BOSTON_CLK_INPUT] = hw; 79*6b0fd6c1SPaul Burton 80*6b0fd6c1SPaul Burton hw = clk_hw_register_fixed_rate(NULL, "sys", "input", 0, sys_freq); 81*6b0fd6c1SPaul Burton if (IS_ERR(hw)) { 82*6b0fd6c1SPaul Burton pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw)); 83*6b0fd6c1SPaul Burton return; 84*6b0fd6c1SPaul Burton } 85*6b0fd6c1SPaul Burton onecell->hws[BOSTON_CLK_SYS] = hw; 86*6b0fd6c1SPaul Burton 87*6b0fd6c1SPaul Burton hw = clk_hw_register_fixed_rate(NULL, "cpu", "input", 0, cpu_freq); 88*6b0fd6c1SPaul Burton if (IS_ERR(hw)) { 89*6b0fd6c1SPaul Burton pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw)); 90*6b0fd6c1SPaul Burton return; 91*6b0fd6c1SPaul Burton } 92*6b0fd6c1SPaul Burton onecell->hws[BOSTON_CLK_CPU] = hw; 93*6b0fd6c1SPaul Burton 94*6b0fd6c1SPaul Burton err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, onecell); 95*6b0fd6c1SPaul Burton if (err) 96*6b0fd6c1SPaul Burton pr_err("failed to add DT provider: %d\n", err); 97*6b0fd6c1SPaul Burton } 98*6b0fd6c1SPaul Burton 99*6b0fd6c1SPaul Burton /* 100*6b0fd6c1SPaul Burton * Use CLK_OF_DECLARE so that this driver is probed early enough to provide the 101*6b0fd6c1SPaul Burton * CPU frequency for use with the GIC or cop0 counters/timers. 102*6b0fd6c1SPaul Burton */ 103*6b0fd6c1SPaul Burton CLK_OF_DECLARE(clk_boston, "img,boston-clock", clk_boston_setup); 104