summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/clk/rockchip/clk-mmc-phase.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/clk/rockchip/clk-mmc-phase.c')
-rw-r--r--kernel/drivers/clk/rockchip/clk-mmc-phase.c74
1 files changed, 51 insertions, 23 deletions
diff --git a/kernel/drivers/clk/rockchip/clk-mmc-phase.c b/kernel/drivers/clk/rockchip/clk-mmc-phase.c
index c842e3b60..268564482 100644
--- a/kernel/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/kernel/drivers/clk/rockchip/clk-mmc-phase.c
@@ -14,7 +14,10 @@
*/
#include <linux/slab.h>
+#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
#include "clk.h"
struct rockchip_mmc_clock {
@@ -38,12 +41,14 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+#define ROCKCHIP_MMC_INIT_STATE_RESET 0x1
+#define ROCKCHIP_MMC_INIT_STATE_SHIFT 1
#define PSECS_PER_SEC 1000000000000LL
/*
- * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
- * simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
+ * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
*/
#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
@@ -66,7 +71,7 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
- degrees += delay_num * factor / 10000;
+ degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
}
return degrees % 360;
@@ -79,25 +84,41 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
u8 nineties, remainder;
u8 delay_num;
u32 raw_value;
- u64 delay;
-
- /* allow 22 to be 22.5 */
- degrees++;
- /* floor to 22.5 increment */
- degrees -= ((degrees) * 10 % 225) / 10;
+ u32 delay;
nineties = degrees / 90;
- /* 22.5 multiples */
- remainder = (degrees % 90) / 22;
-
- delay = PSECS_PER_SEC;
- do_div(delay, rate);
- /* / 360 / 22.5 */
- do_div(delay, 16);
- do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC);
-
+ remainder = (degrees % 90);
+
+ /*
+ * Due to the inexact nature of the "fine" delay, we might
+ * actually go non-monotonic. We don't go _too_ monotonic
+ * though, so we should be OK. Here are options of how we may
+ * work:
+ *
+ * Ideally we end up with:
+ * 1.0, 2.0, ..., 69.0, 70.0, ..., 89.0, 90.0
+ *
+ * On one extreme (if delay is actually 44ps):
+ * .73, 1.5, ..., 50.6, 51.3, ..., 65.3, 90.0
+ * The other (if delay is actually 77ps):
+ * 1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90
+ *
+ * It's possible we might make a delay that is up to 25
+ * degrees off from what we think we're making. That's OK
+ * though because we should be REALLY far from any bad range.
+ */
+
+ /*
+ * Convert to delay; do a little extra work to make sure we
+ * don't overflow 32-bit / 64-bit numbers.
+ */
+ delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
delay *= remainder;
- delay_num = (u8) min(delay, 255ULL);
+ delay = DIV_ROUND_CLOSEST(delay,
+ (rate / 1000) * 36 *
+ (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
+
+ delay_num = (u8) min_t(u32, delay, 255);
raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
@@ -105,7 +126,7 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift), mmc_clock->reg);
pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d\n",
- __clk_get_name(hw->clk), degrees, delay_num,
+ clk_hw_get_name(hw), degrees, delay_num,
mmc_clock->reg, raw_value>>(mmc_clock->shift),
rockchip_mmc_get_phase(hw)
);
@@ -120,7 +141,7 @@ static const struct clk_ops rockchip_mmc_clk_ops = {
};
struct clk *rockchip_clk_register_mmc(const char *name,
- const char **parent_names, u8 num_parents,
+ const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift)
{
struct clk_init_data init;
@@ -131,6 +152,7 @@ struct clk *rockchip_clk_register_mmc(const char *name,
if (!mmc_clock)
return NULL;
+ init.name = name;
init.num_parents = num_parents;
init.parent_names = parent_names;
init.ops = &rockchip_mmc_clk_ops;
@@ -139,8 +161,14 @@ struct clk *rockchip_clk_register_mmc(const char *name,
mmc_clock->reg = reg;
mmc_clock->shift = shift;
- if (name)
- init.name = name;
+ /*
+ * Assert init_state to soft reset the CLKGEN
+ * for mmc tuning phase and degree
+ */
+ if (mmc_clock->shift == ROCKCHIP_MMC_INIT_STATE_SHIFT)
+ writel(HIWORD_UPDATE(ROCKCHIP_MMC_INIT_STATE_RESET,
+ ROCKCHIP_MMC_INIT_STATE_RESET,
+ mmc_clock->shift), mmc_clock->reg);
clk = clk_register(NULL, &mmc_clock->hw);
if (IS_ERR(clk))