From e6de97756aef570dfdc4f0d19ec5792cb29c5f99 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Wed, 8 May 2013 16:17:54 +0800 Subject: [PATCH] rk3188: add delayline support again --- arch/arm/mach-rk3188/Makefile | 1 + arch/arm/mach-rk3188/delayline.c | 109 +++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 arch/arm/mach-rk3188/delayline.c diff --git a/arch/arm/mach-rk3188/Makefile b/arch/arm/mach-rk3188/Makefile index f747a9eae6db..141483d89965 100755 --- a/arch/arm/mach-rk3188/Makefile +++ b/arch/arm/mach-rk3188/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_CPU_IDLE) += ../mach-rk30/cpuidle.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_DVFS) += dvfs.o obj-y += board.o +obj-y += delayline.o board-$(CONFIG_MACH_RK3188_FPGA) += board-rk3188-fpga.o board-$(CONFIG_MACH_RK3188_TB) += ../mach-rk30/board-rk3168-tb.o diff --git a/arch/arm/mach-rk3188/delayline.c b/arch/arm/mach-rk3188/delayline.c new file mode 100644 index 000000000000..d12b388f7d3e --- /dev/null +++ b/arch/arm/mach-rk3188/delayline.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +//#define DEBUG 1 +#define pr_fmt(fmt) "delayline: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define RK30_DELAYLINE_BASE (RK30_TIMER0_BASE + 0x1000) +#define delayline_readl(offset) readl_relaxed(RK30_DELAYLINE_BASE + offset) +#define delayline_writel(val, offset) writel_relaxed(val, RK30_DELAYLINE_BASE + offset) + +#define DELAYLINE_CON0 (0x0000) +#define DELAYLINE_STOP (0 << 5) +#define DELAYLINE_START (1 << 5) +#define DELAYLINE_CLK_SEL_24M (0 << 4) +#define DELAYLINE_CLK_SEL_ACLK_CPU (1 << 4) +#define DELAYLINE_DIV_MAX (0xf) + +#define DELAYLINE_CON1 (0x0004) +#define DELAYLINE_INCREMENT(n) ((n & 0xff) << 8) +#define DELAYLINE_START_POINT(n) (n & 0xff) + +#define DELAYLINE_STATUS (0x0008) +#define DELAYLINE_LOCKED (1 << 8) +#define DELAYLINE_VALUE_MASK (0xff) + +static u32 delayline_div; +static u32 start_point; + +int rk3188_get_delayline_value(void) +{ + u32 status; + u32 loop = 1000; + + if (!start_point) + return 0; + + delayline_writel(DELAYLINE_INCREMENT(4) | start_point, DELAYLINE_CON1); + delayline_writel(DELAYLINE_START | DELAYLINE_CLK_SEL_ACLK_CPU | delayline_div, DELAYLINE_CON0); + dsb(); + delayline_writel(DELAYLINE_STOP | DELAYLINE_CLK_SEL_ACLK_CPU | delayline_div, DELAYLINE_CON0); + dsb(); + + do { + status = delayline_readl(DELAYLINE_STATUS); + if (status & DELAYLINE_LOCKED) + break; + udelay(1); + loop--; + } while (loop); + + if (!loop) { + start_point >>= 1; + return 0; + } + + return status & DELAYLINE_VALUE_MASK; +} + +static int __init delayline_set_rate(unsigned long parent_rate, unsigned long rate) +{ + u32 div; + for (div = 0; div <= DELAYLINE_DIV_MAX; div++) { + u32 new_rate = parent_rate / (div + 1); + if (new_rate <= rate) { + delayline_div = div; + return 0; + } + } + return -ENOENT; +} + +static int __init rk3188_delayline_init(void) +{ + struct clk *clk_aclk_cpu; + unsigned long aclk_rate; + + if (!soc_is_rk3188plus()) + return 0; + + clk_aclk_cpu = clk_get(NULL, "aclk_cpu"); + if (IS_ERR_OR_NULL(clk_aclk_cpu)) { + pr_err("can not get parent clock 'aclk_cpu'\n"); + return 0; + } + + aclk_rate = clk_get_rate(clk_aclk_cpu); + clk_put(clk_aclk_cpu); + delayline_set_rate(aclk_rate, 100 * 1000 * 1000); + start_point = 0x10; + start_point = rk3188_get_delayline_value() >> 1; + printk(KERN_DEBUG "delayline: aclk_cpu %lu div %u clk %lu start point %u\n", aclk_rate, delayline_div, aclk_rate / (delayline_div + 1), start_point); + + return 0; +} + +pure_initcall(rk3188_delayline_init); -- 2.34.1