1/* 2 * Copyright 2014 Google, Inc 3 * Author: Alexandru M Stan <amstan@chromium.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16#include <linux/slab.h> 17#include <linux/clk-provider.h> 18#include "clk.h" 19 20struct rockchip_mmc_clock { 21 struct clk_hw hw; 22 void __iomem *reg; 23 int id; 24 int shift; 25}; 26 27#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw) 28 29#define RK3288_MMC_CLKGEN_DIV 2 30 31static unsigned long rockchip_mmc_recalc(struct clk_hw *hw, 32 unsigned long parent_rate) 33{ 34 return parent_rate / RK3288_MMC_CLKGEN_DIV; 35} 36 37#define ROCKCHIP_MMC_DELAY_SEL BIT(10) 38#define ROCKCHIP_MMC_DEGREE_MASK 0x3 39#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 40#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) 41 42#define PSECS_PER_SEC 1000000000000LL 43 44/* 45 * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to 46 * simplify calculations. So 45degs could be anywhere between 33deg and 66deg. 47 */ 48#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 49 50static int rockchip_mmc_get_phase(struct clk_hw *hw) 51{ 52 struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw); 53 unsigned long rate = clk_get_rate(hw->clk); 54 u32 raw_value; 55 u16 degrees; 56 u32 delay_num = 0; 57 58 raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift); 59 60 degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; 61 62 if (raw_value & ROCKCHIP_MMC_DELAY_SEL) { 63 /* degrees/delaynum * 10000 */ 64 unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) * 65 36 * (rate / 1000000); 66 67 delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); 68 delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; 69 degrees += delay_num * factor / 10000; 70 } 71 72 return degrees % 360; 73} 74 75static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) 76{ 77 struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw); 78 unsigned long rate = clk_get_rate(hw->clk); 79 u8 nineties, remainder; 80 u8 delay_num; 81 u32 raw_value; 82 u64 delay; 83 84 /* allow 22 to be 22.5 */ 85 degrees++; 86 /* floor to 22.5 increment */ 87 degrees -= ((degrees) * 10 % 225) / 10; 88 89 nineties = degrees / 90; 90 /* 22.5 multiples */ 91 remainder = (degrees % 90) / 22; 92 93 delay = PSECS_PER_SEC; 94 do_div(delay, rate); 95 /* / 360 / 22.5 */ 96 do_div(delay, 16); 97 do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC); 98 99 delay *= remainder; 100 delay_num = (u8) min(delay, 255ULL); 101 102 raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; 103 raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; 104 raw_value |= nineties; 105 writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift), mmc_clock->reg); 106 107 pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d\n", 108 __clk_get_name(hw->clk), degrees, delay_num, 109 mmc_clock->reg, raw_value>>(mmc_clock->shift), 110 rockchip_mmc_get_phase(hw) 111 ); 112 113 return 0; 114} 115 116static const struct clk_ops rockchip_mmc_clk_ops = { 117 .recalc_rate = rockchip_mmc_recalc, 118 .get_phase = rockchip_mmc_get_phase, 119 .set_phase = rockchip_mmc_set_phase, 120}; 121 122struct clk *rockchip_clk_register_mmc(const char *name, 123 const char **parent_names, u8 num_parents, 124 void __iomem *reg, int shift) 125{ 126 struct clk_init_data init; 127 struct rockchip_mmc_clock *mmc_clock; 128 struct clk *clk; 129 130 mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL); 131 if (!mmc_clock) 132 return NULL; 133 134 init.num_parents = num_parents; 135 init.parent_names = parent_names; 136 init.ops = &rockchip_mmc_clk_ops; 137 138 mmc_clock->hw.init = &init; 139 mmc_clock->reg = reg; 140 mmc_clock->shift = shift; 141 142 if (name) 143 init.name = name; 144 145 clk = clk_register(NULL, &mmc_clock->hw); 146 if (IS_ERR(clk)) 147 goto err_free; 148 149 return clk; 150 151err_free: 152 kfree(mmc_clock); 153 return NULL; 154} 155