From bfdd732b80a56e31d3bbe59de76a6a91b0f5b9e4 Mon Sep 17 00:00:00 2001 From: huang lin Date: Thu, 25 Sep 2014 16:33:38 +0800 Subject: [PATCH] rockchip: support pwm regulator BUG=None TEST=Boot Veyron Pinky and test the VDD_LOG Original-Change-Id: Ie2eef918e04ba0e13879e915b0b0bef44aef550e Original-Signed-off-by: huang lin Original-Reviewed-on: https://chromium-review.googlesource.com/219753 Original-Reviewed-by: Julius Werner Original-Commit-Queue: Julius Werner Change-Id: I444b47564d90b3480b351fdd8460e5b94e71927c (cherry picked from commit 4491d9c4037161fd8c4cc40856167bf73182fda6) Signed-off-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/9240 Reviewed-by: Patrick Georgi Tested-by: build bot (Jenkins) --- src/mainboard/google/veyron_pinky/romstage.c | 23 +++++ src/soc/rockchip/rk3288/Makefile.inc | 2 + src/soc/rockchip/rk3288/clock.c | 95 ++++++++++++++++++-- src/soc/rockchip/rk3288/clock.h | 8 ++ src/soc/rockchip/rk3288/grf.h | 2 + src/soc/rockchip/rk3288/pwm.c | 90 +++++++++++++++++++ src/soc/rockchip/rk3288/pwm.h | 26 ++++++ 7 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 src/soc/rockchip/rk3288/pwm.c create mode 100644 src/soc/rockchip/rk3288/pwm.h diff --git a/src/mainboard/google/veyron_pinky/romstage.c b/src/mainboard/google/veyron_pinky/romstage.c index 31317a06a4..f972ee26c8 100644 --- a/src/mainboard/google/veyron_pinky/romstage.c +++ b/src/mainboard/google/veyron_pinky/romstage.c @@ -28,11 +28,32 @@ #include #include #include +#include +#include #include #include #include +#include +#include #include "timer.h" +static void regulate_vdd_log(unsigned int mv) +{ + unsigned int duty_ns; + const u32 period_ns = 2000; /* pwm period: 2000ns */ + const u32 max_regulator_mv = 1350; /* 1.35V */ + const u32 min_regulator_mv = 870; /* 0.87V */ + + writel(IOMUX_PWM1, &rk3288_grf->iomux_pwm1); + + assert((mv >= min_regulator_mv) && (mv <= max_regulator_mv)); + + duty_ns = (max_regulator_mv - mv) * period_ns / + (max_regulator_mv - min_regulator_mv); + + pwm_init(1, period_ns, duty_ns); +} + void main(void) { #if CONFIG_COLLECT_TIMESTAMPS @@ -49,6 +70,8 @@ void main(void) console_init(); + /* vdd_log 1200mv is enough for ddr run 666Mhz */ + regulate_vdd_log(1200); #if CONFIG_COLLECT_TIMESTAMPS before_dram_time = timestamp_get(); #endif diff --git a/src/soc/rockchip/rk3288/Makefile.inc b/src/soc/rockchip/rk3288/Makefile.inc index aa1f002fbe..95d72c9782 100644 --- a/src/soc/rockchip/rk3288/Makefile.inc +++ b/src/soc/rockchip/rk3288/Makefile.inc @@ -51,6 +51,7 @@ romstage-y += gpio.c romstage-y += spi.c romstage-y += media.c romstage-y += sdram.c +romstage-y += pwm.c ramstage-y += soc.c ramstage-y += cbmem.c @@ -62,6 +63,7 @@ ramstage-y += spi.c ramstage-y += gpio.c ramstage-y += media.c ramstage-y += rk808.c +ramstage-y += pwm.c ramstage-$(CONFIG_DRIVERS_UART) += uart.c $(objcbfs)/bootblock.raw.elf: $(objcbfs)/bootblock.elf diff --git a/src/soc/rockchip/rk3288/clock.c b/src/soc/rockchip/rk3288/clock.c index 2449674f20..59a32db5b2 100644 --- a/src/soc/rockchip/rk3288/clock.c +++ b/src/soc/rockchip/rk3288/clock.c @@ -109,6 +109,32 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 4); /* m0 core axi clock div: clk = clk_src / (div_con + 1) */ #define M0_DIV_MSK (0xF) +/*******************CLKSEL1 BITS***************************/ +/* pd bus clk pll sel: codec or general */ +#define PD_BUS_SEL_PLL_MSK (1 << 15) +#define PD_BUS_SEL_CPLL (0 << 15) +#define PD_BUS_SEL_GPLL (1 << 15) + +/* pd bus pclk div: + * pclk = pd_bus_aclk /(div + 1) + */ +#define PD_BUS_PCLK_DIV_SHIFT (12) +#define PD_BUS_PCLK_DIV_MSK (0x7 << 12) + +/* pd bus hclk div: + * aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 + */ +#define PD_BUS_HCLK_DIV_SHIFT (8) +#define PD_BUS_HCLK_DIV_MSK (0x3 << 8) + +/* pd bus aclk div: + * pd_bus_aclk = pd_bus_src_clk /(div0 * div1) + */ +#define PD_BUS_ACLK_DIV0_SHIFT (3) +#define PD_BUS_ACLK_DIV0_MASK (0x1f << 3) +#define PD_BUS_ACLK_DIV1_SHIFT (0) +#define PD_BUS_ACLK_DIV1_MASK (0x7 << 0) + /*******************CLKSEL10 BITS***************************/ /* peripheral bus clk pll sel: codec or general */ #define PERI_SEL_PLL_MSK (1 << 15) @@ -133,6 +159,7 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 4); */ #define PERI_ACLK_DIV_SHIFT (0x0) #define PERI_ACLK_DIV_MSK (0x1F) +#define PERI_ACLK_DIV_SHIFT (0) /*******************CLKSEL37 BITS***************************/ #define L2_DIV_MSK (0x7) @@ -186,8 +213,28 @@ static int rkclk_set_pll(u32 *pll_con, const struct pll_div *pll_div_cfg) return 0; } +/* + TODO: + it should be replaced by lib.h function + 'unsigned long log2(unsigned long x)' +*/ +static unsigned int log2(unsigned int value) +{ + unsigned int div = 0; + + while (value != 1) { + div++; + value = ALIGN_UP(value, 2) / 2; + } + return div; +} + void rkclk_init(void) { + u32 aclk_div; + u32 hclk_div; + u32 pclk_div; + /* pll enter slow-mode */ writel(RK_CLRSETBITS(APLL_MODE_MSK, APLL_MODE_SLOW) | RK_CLRSETBITS(GPLL_MODE_MSK, GPLL_MODE_SLOW) @@ -231,16 +278,52 @@ void rkclk_init(void) | RK_CLRSETBITS(PCLK_DBG_DIV_MSK, (3 << PCLK_DBG_DIV_SHIFT)), &cru_ptr->cru_clksel_con[37]); + /* + * pd_bus clock pll source selection and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1; + assert((aclk_div + 1) * PD_BUS_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1; + assert((hclk_div + 1) * PD_BUS_HCLK_HZ == + PD_BUS_ACLK_HZ && (hclk_div < 0x4) && (hclk_div != 0x2)); + + pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1; + assert((pclk_div + 1) * PD_BUS_PCLK_HZ == + PD_BUS_ACLK_HZ && pclk_div < 0x7); + + writel(RK_SETBITS(PD_BUS_SEL_GPLL) + | RK_CLRSETBITS(PD_BUS_PCLK_DIV_MSK, + pclk_div << PD_BUS_PCLK_DIV_SHIFT) + | RK_CLRSETBITS(PD_BUS_HCLK_DIV_MSK, + hclk_div << PD_BUS_HCLK_DIV_SHIFT) + | RK_CLRSETBITS(PD_BUS_ACLK_DIV0_MASK, + aclk_div << PD_BUS_ACLK_DIV0_SHIFT) + | RK_CLRSETBITS(PD_BUS_ACLK_DIV1_MASK, 0 << 0), + &cru_ptr->cru_clksel_con[1]); + /* * peri clock pll source selection and * set up dependent divisors for PCLK/HCLK and ACLK clocks. - * peri clock select gpll, gpll clk = 594MHz - * aclk = 148.5MHz, hclk = 148.5Mhz, pclk = 74.25MHz */ - writel(RK_SETBITS(PERI_SEL_PLL_MSK) - | RK_CLRSETBITS(PERI_PCLK_DIV_MSK, 1 << PERI_PCLK_DIV_SHIFT) - | RK_CLRSETBITS(PERI_HCLK_DIV_MSK, 0 << PERI_HCLK_DIV_SHIFT) - | RK_CLRSETBITS(PERI_ACLK_DIV_MSK, 3 << 0), + aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; + assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + + hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ); + assert((1 << hclk_div) * PERI_HCLK_HZ == + PERI_ACLK_HZ && (hclk_div < 0x4)); + + pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ); + assert((1 << pclk_div) * PERI_PCLK_HZ == + PERI_ACLK_HZ && (pclk_div < 0x4)); + + writel(RK_SETBITS(PERI_SEL_GPLL) + | RK_CLRSETBITS(PERI_PCLK_DIV_MSK, + pclk_div << PERI_PCLK_DIV_SHIFT) + | RK_CLRSETBITS(PERI_HCLK_DIV_MSK, + hclk_div << PERI_HCLK_DIV_SHIFT) + | RK_CLRSETBITS(PERI_ACLK_DIV_MSK, + aclk_div << PERI_ACLK_DIV_SHIFT), &cru_ptr->cru_clksel_con[10]); /* PLL enter normal-mode */ diff --git a/src/soc/rockchip/rk3288/clock.h b/src/soc/rockchip/rk3288/clock.h index 452d352194..b206baa006 100644 --- a/src/soc/rockchip/rk3288/clock.h +++ b/src/soc/rockchip/rk3288/clock.h @@ -26,6 +26,14 @@ #define GPLL_HZ 594000000 #define CPLL_HZ 384000000 +#define PD_BUS_ACLK_HZ 148500000 +#define PD_BUS_HCLK_HZ 148500000 +#define PD_BUS_PCLK_HZ 74250000 + +#define PERI_ACLK_HZ 148500000 +#define PERI_HCLK_HZ 148500000 +#define PERI_PCLK_HZ 74250000 + void rkclk_init(void); void rkclk_configure_spi(unsigned int bus, unsigned int hz); void rkclk_ddr_reset(u32 ch, u32 ctl, u32 phy); diff --git a/src/soc/rockchip/rk3288/grf.h b/src/soc/rockchip/rk3288/grf.h index e0dfc02361..e84359bfe0 100644 --- a/src/soc/rockchip/rk3288/grf.h +++ b/src/soc/rockchip/rk3288/grf.h @@ -87,6 +87,7 @@ struct rk3288_grf_regs { union { u32 gpio7a_iomux; u32 iomux_pwm0; + u32 iomux_pwm1; }; u32 gpio7b_iomux; union { @@ -215,4 +216,5 @@ static struct rk3288_sgrf_regs * const rk3288_sgrf = (void *)GRF_SECURE_BASE; 2 << 2 | 2 << 0) #define IOMUX_EMMCPWREN RK_CLRSETBITS(0x3 << 2, 0x2 << 2) #define IOMUX_EMMCCMD RK_CLRSETBITS(0x3f, 2 << 4 | 2 << 2 | 2 << 0) +#define IOMUX_PWM1 RK_SETBITS(1 << 2) #endif diff --git a/src/soc/rockchip/rk3288/pwm.c b/src/soc/rockchip/rk3288/pwm.c new file mode 100644 index 0000000000..7f659f2f38 --- /dev/null +++ b/src/soc/rockchip/rk3288/pwm.c @@ -0,0 +1,90 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "addressmap.h" +#include "grf.h" +#include "soc.h" +#include "pwm.h" +#include "clock.h" + +struct pwm_ctl { + u32 pwm_cnt; + u32 pwm_period_hpr; + u32 pwm_duty_lpr; + u32 pwm_ctrl; +}; + +struct rk3288_pwm_regs { + struct pwm_ctl pwm[4]; + u32 intsts; + u32 int_en; +}; +check_member(rk3288_pwm_regs, int_en, 0x44); + +#define RK_PWM_DISABLE (0 << 0) +#define RK_PWM_ENABLE (1 << 0) + + +#define PWM_ONE_SHOT (0 << 1) +#define PWM_CONTINUOUS (1 << 1) +#define RK_PWM_CAPTURE (1 << 2) + +#define PWM_DUTY_POSTIVE (1 << 3) +#define PWM_DUTY_NEGATIVE (0 << 3) + +#define PWM_INACTIVE_POSTIVE (1 << 4) +#define PWM_INACTIVE_NEGATIVE (0 << 4) + +#define PWM_OUTPUT_LEFT (0 << 5) +#define PWM_OUTPUT_CENTER (1 << 5) + +#define PWM_LP_ENABLE (1 << 8) +#define PWM_LP_DISABLE (0 << 8) + +#define PWM_SEL_SCALE_CLK (1 << 9) +#define PWM_SEL_SRC_CLK (0 << 9) + +struct rk3288_pwm_regs *rk3288_pwm = (void *)RK_PWM0123_BASE; + +void pwm_init(u32 id, u32 period_ns, u32 duty_ns) +{ + unsigned long period, duty; + + /*use rk pwm*/ + writel(RK_SETBITS(1 << 0), &rk3288_grf->soc_con2); + + writel(PWM_SEL_SRC_CLK | PWM_OUTPUT_LEFT | PWM_LP_DISABLE | + PWM_CONTINUOUS | PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE | + RK_PWM_DISABLE, + &rk3288_pwm->pwm[id].pwm_ctrl); + + period = (PD_BUS_PCLK_HZ / 1000) * period_ns / USECS_PER_SEC; + duty = (PD_BUS_PCLK_HZ / 1000) * duty_ns / USECS_PER_SEC; + + writel(period, &rk3288_pwm->pwm[id].pwm_period_hpr); + writel(duty, &rk3288_pwm->pwm[id].pwm_duty_lpr); + setbits_le32(&rk3288_pwm->pwm[id].pwm_ctrl, RK_PWM_ENABLE); +} diff --git a/src/soc/rockchip/rk3288/pwm.h b/src/soc/rockchip/rk3288/pwm.h new file mode 100644 index 0000000000..168d8a77d4 --- /dev/null +++ b/src/soc/rockchip/rk3288/pwm.h @@ -0,0 +1,26 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_ROCKCHIP_RK3288_PWM_H__ +#define __SOC_ROCKCHIP_RK3288_PWM_H__ + +void pwm_init(u32 id, u32 period_ns, u32 duty_ns); + +#endif +