From: Saravana Kannan <skannan@xxxxxxxxxxxxxx> Support locally controllable clocks on the 7x30 target. Signed-off-by: Saravana Kannan <skannan@xxxxxxxxxxxxxx> Signed-off-by: Stephen Boyd <sboyd@xxxxxxxxxxxxxx> --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/clock-7x30.c | 1550 ++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-msm/clock-7x30.h | 38 +- arch/arm/mach-msm/clock.c | 13 +- arch/arm/mach-msm/clock.h | 12 + arch/arm/mach-msm/proc_comm.h | 1 + 6 files changed, 1599 insertions(+), 16 deletions(-) create mode 100644 arch/arm/mach-msm/clock-7x30.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index dc8ef08..8588b57 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -2,6 +2,7 @@ obj-y += io.o idle.o timer.o ifdef CONFIG_MSM_PROC_COMM obj-$(CONFIG_DEBUG_FS) += clock-debug.o endif +obj-$(CONFIG_ARCH_MSM7X30) += clock-7x30.o ifndef CONFIG_ARCH_MSM8X60 obj-y += acpuclock-arm11.o obj-y += dma.o diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c new file mode 100644 index 0000000..beca3a6 --- /dev/null +++ b/arch/arm/mach-msm/clock-7x30.c @@ -0,0 +1,1550 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/ctype.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/delay.h> + +#include <mach/msm_iomap.h> +#include <mach/clk.h> + +#include "clock.h" +#include "clock-7x30.h" +#include "clock-pcom.h" +#include "proc_comm.h" + +#define REG_BASE(off) (MSM_CLK_CTL_BASE + off) +#define REG(off) (MSM_CLK_CTL_SH2_BASE + off) +#define MNCNTR_EN_MASK B(8) +#define MNCNTR_RST_MASK B(7) +#define MNCNTR_MODE_MASK BM(6, 5) +#define MNCNTR_MODE BVAL(6, 5, 0x2) /* Dual-edge mode. */ + +/* Shadow-region 2 (SH2) registers. */ +#define QUP_I2C_NS_REG REG(0x04F0) +#define CAM_NS_REG REG(0x0374) +#define CAM_VFE_NS_REG REG(0x0044) +#define CLK_HALT_STATEA_REG REG(0x0108) +#define CLK_HALT_STATEB_REG REG(0x010C) +#define CLK_HALT_STATEC_REG REG(0x02D4) +#define CSI_NS_REG REG(0x0174) +#define EMDH_NS_REG REG(0x0050) +#define GLBL_CLK_ENA_2_SC_REG REG(0x03C0) +#define GLBL_CLK_ENA_SC_REG REG(0x03BC) +#define GLBL_CLK_STATE_2_REG REG(0x037C) +#define GLBL_CLK_STATE_REG REG(0x0004) +#define GRP_2D_NS_REG REG(0x0034) +#define GRP_NS_REG REG(0x0084) +#define HDMI_NS_REG REG(0x0484) +#define I2C_2_NS_REG REG(0x02D8) +#define I2C_NS_REG REG(0x0068) +#define JPEG_NS_REG REG(0x0164) +#define LPA_CORE_CLK_MA0_REG REG(0x04F4) +#define LPA_CORE_CLK_MA2_REG REG(0x04FC) +#define LPA_NS_REG REG(0x02E8) +#define MDC_NS_REG REG(0x007C) +#define MDP_LCDC_NS_REG REG(0x0390) +#define MDP_NS_REG REG(0x014C) +#define MDP_VSYNC_REG REG(0x0460) +#define MFC_NS_REG REG(0x0154) +#define MI2S_CODEC_RX_DIV_REG REG(0x02EC) +#define MI2S_CODEC_TX_DIV_REG REG(0x02F0) +#define MI2S_DIV_REG REG(0x02E4) +#define MI2S_NS_REG REG(0x02E0) +#define MI2S_RX_NS_REG REG(0x0070) +#define MI2S_TX_NS_REG REG(0x0078) +#define MIDI_NS_REG REG(0x02D0) +#define PLL_ENA_REG REG(0x0264) +#define PMDH_NS_REG REG(0x008C) +#define SDAC_NS_REG REG(0x009C) +#define SDCn_NS_REG(n) REG(0x00A4+(0x8*((n)-1))) +#define SPI_NS_REG REG(0x02C8) +#define TSIF_NS_REG REG(0x00C4) +#define TV_NS_REG REG(0x00CC) +#define UART1DM_NS_REG REG(0x00D4) +#define UART2DM_NS_REG REG(0x00DC) +#define UART2_NS_REG REG(0x0464) +#define UART_NS_REG REG(0x00E0) +#define USBH2_NS_REG REG(0x046C) +#define USBH3_NS_REG REG(0x0470) +#define USBH_MD_REG REG(0x02BC) +#define USBH_NS_REG REG(0x02C0) +#define VPE_NS_REG REG(0x015C) + +/* Registers in the base (non-shadow) region. */ +#define PLL0_STATUS_BASE_REG REG_BASE(0x0318) +#define PLL1_STATUS_BASE_REG REG_BASE(0x0334) +#define PLL2_STATUS_BASE_REG REG_BASE(0x0350) +#define PLL3_STATUS_BASE_REG REG_BASE(0x036C) +#define PLL4_STATUS_BASE_REG REG_BASE(0x0254) +#define PLL5_STATUS_BASE_REG REG_BASE(0x0258) +#define PLL6_STATUS_BASE_REG REG_BASE(0x04EC) +#define SH2_OWN_APPS1_BASE_REG REG_BASE(0x040C) +#define SH2_OWN_APPS2_BASE_REG REG_BASE(0x0414) +#define SH2_OWN_APPS3_BASE_REG REG_BASE(0x0444) +#define SH2_OWN_GLBL_BASE_REG REG_BASE(0x0404) +#define SH2_OWN_ROW1_BASE_REG REG_BASE(0x041C) +#define SH2_OWN_ROW2_BASE_REG REG_BASE(0x0424) + +struct clk_freq_tbl { + uint32_t freq_hz; + uint32_t src; + uint32_t md_val; + uint32_t ns_val; + uint32_t mode; + unsigned msmc1; +}; + +struct clk_local { + uint32_t count; + uint32_t type; + void __iomem *md_reg; + void __iomem *ns_reg; + uint32_t freq_mask; + uint32_t br_en_mask; + uint32_t root_en_mask; + int parent; + uint32_t *children; + const struct clk_freq_tbl *freq_tbl; + const struct clk_freq_tbl *current_freq; + void __iomem *halt_reg; + uint32_t halt_mask; + uint32_t test_vector; +}; + + +enum { + SRC_PLL0 = 4, /* Modem PLL */ + SRC_PLL1 = 1, /* Global PLL */ + SRC_PLL3 = 3, /* Multimedia/Peripheral PLL or Backup PLL1 */ + SRC_PLL4 = 2, /* Display PLL */ + SRC_LPXO = 6, /* Low power XO. */ + SRC_TCXO = 0, /* Used for sources that always source from tcxo */ + SRC_AXI = 100, /* Used for rates that sync to AXI */ + SRC_NONE /* Used for sources that can't be turned on/off. */ +}; + +static uint32_t src_pll_tbl[] = { + [SRC_PLL0] = PLL_0, + [SRC_PLL1] = PLL_1, + [SRC_PLL3] = PLL_3, + [SRC_PLL4] = PLL_4, +}; + +#define B(x) BIT(x) +#define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) +#define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) + +#define MD8(m, n) (BVAL(15, 8, m) | BVAL(7, 0, ~(n))) +#define N8(msb, lsb, m, n) (BVAL(msb, lsb, ~(n-m))) +#define MD16(m, n) (BVAL(31, 16, m) | BVAL(15, 0, ~(n))) +#define N16(m, n) (BVAL(31, 16, ~(n-m))) +#define SPDIV(s, d) (BVAL(4, 3, d-1) | BVAL(2, 0, s)) +#define SDIV(s, d) (BVAL(6, 3, d-1) | BVAL(2, 0, s)) +#define F_MASK_BASIC (BM(6, 3)|BM(2, 0)) +#define F_MASK_MND16 (BM(31, 16)|BM(4, 3)|BM(2, 0)) +#define F_MASK_MND8(m, l) (BM(m, l)|BM(4, 3)|BM(2, 0)) + +#define F_RAW(f, s, m_v, n_v, mde, v) { \ + .freq_hz = f, \ + .src = s, \ + .md_val = m_v, \ + .ns_val = n_v, \ + .mode = mde, \ + .msmc1 = v \ + } + +#define FREQ_END 0 +#define F_BASIC(f, s, div, v) F_RAW(f, s, 0, SDIV(s, div), 0, v) +#define F_MND16(f, s, div, m, n, v) \ + F_RAW(f, s, MD16(m, n), N16(m, n)|SPDIV(s, div), !!(n), v) +#define F_MND8(f, nmsb, nlsb, s, div, m, n, v) \ + F_RAW(f, s, MD8(m, n), N8(nmsb, nlsb, m, n)|SPDIV(s, div), !!(n), v) +#define F_END F_RAW(FREQ_END, SRC_NONE, 0, 0, 0, MSMC1_END) + +static const struct clk_freq_tbl clk_tbl_csi[] = { + F_MND8(153600000, 24, 17, SRC_PLL1, 2, 2, 5, NOMINAL), + F_MND8(192000000, 24, 17, SRC_PLL1, 4, 0, 0, NOMINAL), + F_MND8(384000000, 24, 17, SRC_PLL1, 2, 0, 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_tcxo[] = { + F_RAW(19200000, SRC_TCXO, 0, 0, 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_axi[] = { + F_RAW(1, SRC_AXI, 0, 0, 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_uartdm[] = { + F_MND16( 3686400, SRC_PLL3, 3, 3, 200, NOMINAL), + F_MND16( 7372800, SRC_PLL3, 3, 3, 100, NOMINAL), + F_MND16(14745600, SRC_PLL3, 3, 3, 50, NOMINAL), + F_MND16(32000000, SRC_PLL3, 3, 25, 192, NOMINAL), + F_MND16(40000000, SRC_PLL3, 3, 125, 768, NOMINAL), + F_MND16(46400000, SRC_PLL3, 3, 145, 768, NOMINAL), + F_MND16(48000000, SRC_PLL3, 3, 25, 128, NOMINAL), + F_MND16(51200000, SRC_PLL3, 3, 5, 24, NOMINAL), + F_MND16(56000000, SRC_PLL3, 3, 175, 768, NOMINAL), + F_MND16(58982400, SRC_PLL3, 3, 6, 25, NOMINAL), + F_MND16(64000000, SRC_PLL1, 4, 1, 3, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mdh[] = { + F_BASIC( 49150000, SRC_PLL3, 15, NOMINAL), + F_BASIC( 92160000, SRC_PLL3, 8, NOMINAL), + F_BASIC(122880000, SRC_PLL3, 6, NOMINAL), + F_BASIC(184320000, SRC_PLL3, 4, NOMINAL), + F_BASIC(245760000, SRC_PLL3, 3, NOMINAL), + F_BASIC(368640000, SRC_PLL3, 2, NOMINAL), + F_BASIC(384000000, SRC_PLL1, 2, NOMINAL), + F_BASIC(445500000, SRC_PLL4, 2, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_grp[] = { + F_BASIC( 24576000, SRC_LPXO, 1, NOMINAL), + F_BASIC( 46080000, SRC_PLL3, 16, NOMINAL), + F_BASIC( 49152000, SRC_PLL3, 15, NOMINAL), + F_BASIC( 52662875, SRC_PLL3, 14, NOMINAL), + F_BASIC( 56713846, SRC_PLL3, 13, NOMINAL), + F_BASIC( 61440000, SRC_PLL3, 12, NOMINAL), + F_BASIC( 67025454, SRC_PLL3, 11, NOMINAL), + F_BASIC( 73728000, SRC_PLL3, 10, NOMINAL), + F_BASIC( 81920000, SRC_PLL3, 9, NOMINAL), + F_BASIC( 92160000, SRC_PLL3, 8, NOMINAL), + F_BASIC(105325714, SRC_PLL3, 7, NOMINAL), + F_BASIC(122880000, SRC_PLL3, 6, NOMINAL), + F_BASIC(147456000, SRC_PLL3, 5, NOMINAL), + F_BASIC(184320000, SRC_PLL3, 4, NOMINAL), + F_BASIC(192000000, SRC_PLL1, 4, NOMINAL), + F_BASIC(245760000, SRC_PLL3, 3, HIGH), + /* Sync to AXI. Hence this "rate" is not fixed. */ + F_RAW(1, SRC_AXI, 0, B(14), 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_sdc1_3[] = { + F_MND8( 144000, 19, 12, SRC_LPXO, 1, 1, 171, NOMINAL), + F_MND8( 400000, 19, 12, SRC_LPXO, 1, 2, 123, NOMINAL), + F_MND8(16027000, 19, 12, SRC_PLL3, 3, 14, 215, NOMINAL), + F_MND8(17000000, 19, 12, SRC_PLL3, 4, 19, 206, NOMINAL), + F_MND8(20480000, 19, 12, SRC_PLL3, 4, 23, 212, NOMINAL), + F_MND8(24576000, 19, 12, SRC_LPXO, 1, 0, 0, NOMINAL), + F_MND8(49152000, 19, 12, SRC_PLL3, 3, 1, 5, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_sdc2_4[] = { + F_MND8( 144000, 20, 13, SRC_LPXO, 1, 1, 171, NOMINAL), + F_MND8( 400000, 20, 13, SRC_LPXO, 1, 2, 123, NOMINAL), + F_MND8(16027000, 20, 13, SRC_PLL3, 3, 14, 215, NOMINAL), + F_MND8(17000000, 20, 13, SRC_PLL3, 4, 19, 206, NOMINAL), + F_MND8(20480000, 20, 13, SRC_PLL3, 4, 23, 212, NOMINAL), + F_MND8(24576000, 20, 13, SRC_LPXO, 1, 0, 0, NOMINAL), + F_MND8(49152000, 20, 13, SRC_PLL3, 3, 1, 5, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mdp_core[] = { + F_BASIC( 46080000, SRC_PLL3, 16, NOMINAL), + F_BASIC( 49152000, SRC_PLL3, 15, NOMINAL), + F_BASIC( 52663000, SRC_PLL3, 14, NOMINAL), + F_BASIC( 92160000, SRC_PLL3, 8, NOMINAL), + F_BASIC(122880000, SRC_PLL3, 6, NOMINAL), + F_BASIC(147456000, SRC_PLL3, 5, NOMINAL), + F_BASIC(153600000, SRC_PLL1, 5, NOMINAL), + F_BASIC(192000000, SRC_PLL1, 4, HIGH), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mdp_lcdc[] = { + F_MND16(24576000, SRC_LPXO, 1, 0, 0, NOMINAL), + F_MND16(30720000, SRC_PLL3, 4, 1, 6, NOMINAL), + F_MND16(32768000, SRC_PLL3, 3, 2, 15, NOMINAL), + F_MND16(40960000, SRC_PLL3, 2, 1, 9, NOMINAL), + F_MND16(73728000, SRC_PLL3, 2, 1, 5, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mdp_vsync[] = { + F_RAW(24576000, SRC_LPXO, 0, 0, 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mi2s_codec[] = { + F_MND16( 2048000, SRC_LPXO, 4, 1, 3, NOMINAL), + F_MND16(12288000, SRC_LPXO, 2, 0, 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mi2s[] = { + F_MND16(12288000, SRC_LPXO, 2, 0, 0, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_midi[] = { + F_MND8(98304000, 19, 12, SRC_PLL3, 3, 2, 5, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_sdac[] = { + F_MND16( 256000, SRC_LPXO, 4, 1, 24, NOMINAL), + F_MND16( 352800, SRC_LPXO, 1, 147, 10240, NOMINAL), + F_MND16( 384000, SRC_LPXO, 4, 1, 16, NOMINAL), + F_MND16( 512000, SRC_LPXO, 4, 1, 12, NOMINAL), + F_MND16( 705600, SRC_LPXO, 1, 147, 5120, NOMINAL), + F_MND16( 768000, SRC_LPXO, 4, 1, 8, NOMINAL), + F_MND16(1024000, SRC_LPXO, 4, 1, 6, NOMINAL), + F_MND16(1411200, SRC_LPXO, 1, 147, 2560, NOMINAL), + F_MND16(1536000, SRC_LPXO, 4, 1, 4, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_tv[] = { + F_MND8(27000000, 23, 16, SRC_PLL4, 2, 2, 33, NOMINAL), + F_MND8(74250000, 23, 16, SRC_PLL4, 2, 1, 6, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_usb[] = { + F_MND8(60000000, 23, 16, SRC_PLL1, 2, 5, 32, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_vfe_jpeg[] = { + F_MND16( 36864000, SRC_PLL3, 4, 1, 5, NOMINAL), + F_MND16( 46080000, SRC_PLL3, 4, 1, 4, NOMINAL), + F_MND16( 61440000, SRC_PLL3, 4, 1, 3, NOMINAL), + F_MND16( 73728000, SRC_PLL3, 2, 1, 5, NOMINAL), + F_MND16( 81920000, SRC_PLL3, 3, 1, 3, NOMINAL), + F_MND16( 92160000, SRC_PLL3, 4, 1, 2, NOMINAL), + F_MND16( 98304000, SRC_PLL3, 3, 2, 5, NOMINAL), + F_MND16(105326000, SRC_PLL3, 2, 2, 7, NOMINAL), + F_MND16(122880000, SRC_PLL3, 2, 1, 3, NOMINAL), + F_MND16(147456000, SRC_PLL3, 2, 2, 5, NOMINAL), + F_MND16(153600000, SRC_PLL1, 2, 2, 5, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_cam[] = { + F_MND16( 6000000, SRC_PLL1, 4, 1, 32, NOMINAL), + F_MND16( 8000000, SRC_PLL1, 4, 1, 24, NOMINAL), + F_MND16(12000000, SRC_PLL1, 4, 1, 16, NOMINAL), + F_MND16(16000000, SRC_PLL1, 4, 1, 12, NOMINAL), + F_MND16(19200000, SRC_PLL1, 4, 1, 10, NOMINAL), + F_MND16(24000000, SRC_PLL1, 4, 1, 8, NOMINAL), + F_MND16(32000000, SRC_PLL1, 4, 1, 6, NOMINAL), + F_MND16(48000000, SRC_PLL1, 4, 1, 4, NOMINAL), + F_MND16(64000000, SRC_PLL1, 4, 1, 3, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_vpe[] = { + F_MND8( 24576000, 22, 15, SRC_LPXO, 1, 0, 0, NOMINAL), + F_MND8( 30720000, 22, 15, SRC_PLL3, 4, 1, 6, NOMINAL), + F_MND8( 61440000, 22, 15, SRC_PLL3, 4, 1, 3, NOMINAL), + F_MND8( 81920000, 22, 15, SRC_PLL3, 3, 1, 3, NOMINAL), + F_MND8(122880000, 22, 15, SRC_PLL3, 3, 1, 2, NOMINAL), + F_MND8(147456000, 22, 15, SRC_PLL3, 1, 1, 5, NOMINAL), + F_MND8(153600000, 22, 15, SRC_PLL1, 1, 1, 5, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_mfc[] = { + F_MND8( 24576000, 24, 17, SRC_LPXO, 1, 0, 0, NOMINAL), + F_MND8( 30720000, 24, 17, SRC_PLL3, 4, 1, 6, NOMINAL), + F_MND8( 61440000, 24, 17, SRC_PLL3, 4, 1, 3, NOMINAL), + F_MND8( 81920000, 24, 17, SRC_PLL3, 3, 1, 3, NOMINAL), + F_MND8(122880000, 24, 17, SRC_PLL3, 3, 1, 2, NOMINAL), + F_MND8(147456000, 24, 17, SRC_PLL3, 1, 1, 5, NOMINAL), + F_MND8(153600000, 24, 17, SRC_PLL1, 1, 1, 5, NOMINAL), + F_MND8(170667000, 24, 17, SRC_PLL1, 1, 2, 9, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_spi[] = { + F_MND8( 9963243, 19, 12, SRC_PLL3, 4, 7, 129, NOMINAL), + F_MND8(26331429, 19, 12, SRC_PLL3, 4, 34, 241, NOMINAL), + F_END, +}; + +static const struct clk_freq_tbl clk_tbl_lpa_codec[] = { + F_RAW(1, SRC_NONE, 0, 0, 0, MSMC1_END), /* src = MI2S_CODEC_RX */ + F_RAW(2, SRC_NONE, 0, 1, 0, MSMC1_END), /* src = ECODEC_CIF */ + F_RAW(3, SRC_NONE, 0, 2, 0, MSMC1_END), /* src = MI2S */ + F_RAW(4, SRC_NONE, 0, 3, 0, MSMC1_END), /* src = SDAC */ + F_END, +}; + +static struct clk_freq_tbl dummy_freq = F_END; + +#define MND 1 /* Integer predivider and fractional MN:D divider. */ +#define BASIC 2 /* Integer divider. */ +#define NORATE 3 /* Just on/off. */ + +#define C(x) L_7X30_##x##_CLK + +#define CLK_LOCAL(id, t, md, ns, f_msk, br, root, tbl, \ + par, chld_lst, h, hm, tv) \ + [C(id)] = { \ + .type = t, \ + .md_reg = md, \ + .ns_reg = ns, \ + .freq_mask = f_msk, \ + .br_en_mask = br, \ + .root_en_mask = root, \ + .parent = C(par), \ + .children = chld_lst, \ + .freq_tbl = tbl, \ + .current_freq = &dummy_freq, \ + .halt_reg = h, \ + .halt_mask = hm, \ + .test_vector = tv, \ + } + +#define CLK_BASIC(id, ns, br, root, tbl, par, h, hm, tv) \ + CLK_LOCAL(id, BASIC, 0, ns, F_MASK_BASIC, br, root, tbl, \ + par, NULL, h, hm, tv) +#define CLK_MND8_P(id, ns, m, l, br, root, tbl, par, chld_lst, h, hm, tv) \ + CLK_LOCAL(id, MND, (ns-4), ns, F_MASK_MND8(m, l), br, root, \ + tbl, par, chld_lst, h, hm, tv) +#define CLK_MND8(id, ns, m, l, br, root, tbl, chld_lst, h, hm, tv) \ + CLK_MND8_P(id, ns, m, l, br, root, tbl, NONE, chld_lst, \ + h, hm, tv) +#define CLK_MND16(id, ns, br, root, tbl, par, chld_lst, h, hm, tv) \ + CLK_LOCAL(id, MND, (ns-4), ns, F_MASK_MND16, br, root, tbl, \ + par, chld_lst, h, hm, tv) +#define CLK_1RATE(id, ns, br, root, tbl, h, hm, tv) \ + CLK_LOCAL(id, BASIC, 0, ns, 0, br, root, tbl, NONE, NULL, \ + h, hm, tv) +#define CLK_SLAVE(id, ns, br, par, h, hm, tv) \ + CLK_LOCAL(id, NORATE, 0, ns, 0, br, 0, NULL, par, NULL, \ + h, hm, tv) +#define CLK_NORATE(id, ns, br, root, h, hm, tv) \ + CLK_LOCAL(id, NORATE, 0, ns, 0, br, root, NULL, NONE, NULL, \ + h, hm, tv) +#define CLK_GLBL(id, glbl, br, h, hm, tv) \ + CLK_LOCAL(id, NORATE, 0, glbl, 0, br, 0, NULL, GLBL_ROOT, \ + NULL, h, hm, tv) +#define CLK_BRIDGE(id, glbl, br, par, h, hm, tv) \ + CLK_LOCAL(id, NORATE, 0, glbl, 0, br, 0, NULL, par, NULL, \ + h, hm, tv) + +static uint32_t *pll_status_addr[NUM_PLL] = { + [PLL_0] = PLL0_STATUS_BASE_REG, + [PLL_1] = PLL1_STATUS_BASE_REG, + [PLL_2] = PLL2_STATUS_BASE_REG, + [PLL_3] = PLL3_STATUS_BASE_REG, + [PLL_4] = PLL4_STATUS_BASE_REG, + [PLL_5] = PLL5_STATUS_BASE_REG, + [PLL_6] = PLL6_STATUS_BASE_REG, +}; + +static uint32_t pll_count[NUM_PLL]; + +static uint32_t chld_grp_3d_src[] = {C(IMEM), C(GRP_3D), C(NONE)}; +static uint32_t chld_mdp_lcdc_p[] = {C(MDP_LCDC_PAD_PCLK), C(NONE)}; +static uint32_t chld_mfc[] = {C(MFC_DIV2), C(NONE)}; +static uint32_t chld_mi2s_codec_rx[] = {C(MI2S_CODEC_RX_S), C(NONE)}; +static uint32_t chld_mi2s_codec_tx[] = {C(MI2S_CODEC_TX_S), C(NONE)}; +static uint32_t chld_mi2s[] = {C(MI2S_S), C(NONE)}; +static uint32_t chld_sdac[] = {C(SDAC_M), C(NONE)}; +static uint32_t chld_tv[] = {C(TV_DAC), C(TV_ENC), C(TSIF_REF), + C(HDMI), C(NONE)}; +static uint32_t chld_usb_src[] = {C(USB_HS), C(USB_HS_CORE), + C(USB_HS2), C(USB_HS2_CORE), + C(USB_HS3), C(USB_HS3_CORE), + C(NONE)}; +static uint32_t chld_vfe[] = {C(VFE_MDC), C(VFE_CAMIF), C(CSI0_VFE), + C(NONE)}; + +static struct clk_local clk_local_tbl[] = { + CLK_NORATE(MDC, MDC_NS_REG, B(9), B(11), + CLK_HALT_STATEA_REG, 10, 0x4D56), + CLK_NORATE(LPA_CORE, LPA_NS_REG, B(5), 0, + CLK_HALT_STATEC_REG, 5, 0x0E), + + CLK_1RATE(I2C, I2C_NS_REG, B(9), B(11), clk_tbl_tcxo, + CLK_HALT_STATEA_REG, 15, 0x4D4D), + CLK_1RATE(I2C_2, I2C_2_NS_REG, B(0), B(2), clk_tbl_tcxo, + CLK_HALT_STATEC_REG, 2, 0x0B), + CLK_1RATE(QUP_I2C, QUP_I2C_NS_REG, B(0), B(2), clk_tbl_tcxo, + CLK_HALT_STATEB_REG, 31, 0x1C), + CLK_1RATE(UART1, UART_NS_REG, B(5), B(4), clk_tbl_tcxo, + CLK_HALT_STATEB_REG, 7, 0x4D6F), + CLK_1RATE(UART2, UART2_NS_REG, B(5), B(4), clk_tbl_tcxo, + CLK_HALT_STATEB_REG, 5, 0x4D71), + + CLK_BASIC(EMDH, EMDH_NS_REG, 0, B(11), clk_tbl_mdh, AXI_LI_ADSP_A, + NULL, 0, 0x4F00), + CLK_BASIC(PMDH, PMDH_NS_REG, 0, B(11), clk_tbl_mdh, AXI_LI_ADSP_A, + NULL, 0, 0x5500), + CLK_BASIC(MDP, MDP_NS_REG, B(9), B(11), clk_tbl_mdp_core, AXI_MDP, + CLK_HALT_STATEB_REG, 16, 0x5400), + + CLK_MND8_P(VPE, VPE_NS_REG, 22, 15, B(9), B(11), clk_tbl_vpe, + AXI_VPE, NULL, CLK_HALT_STATEC_REG, 10, 0x6C00), + CLK_MND8_P(MFC, MFC_NS_REG, 24, 17, B(9), B(11), clk_tbl_mfc, + AXI_MFC, chld_mfc, CLK_HALT_STATEC_REG, + 12, 0x38), + CLK_SLAVE(MFC_DIV2, MFC_NS_REG, B(15), MFC, CLK_HALT_STATEC_REG, + 11, 0x1F), + + CLK_MND8(SDC1, SDCn_NS_REG(1), 19, 12, B(9), B(11), clk_tbl_sdc1_3, + NULL, CLK_HALT_STATEA_REG, 1, 0x4D62), + CLK_MND8(SDC2, SDCn_NS_REG(2), 20, 13, B(9), B(11), clk_tbl_sdc2_4, + NULL, CLK_HALT_STATEA_REG, 0, 0x4D64), + CLK_MND8(SDC3, SDCn_NS_REG(3), 19, 12, B(9), B(11), clk_tbl_sdc1_3, + NULL, CLK_HALT_STATEB_REG, 24, 0x4D7A), + CLK_MND8(SDC4, SDCn_NS_REG(4), 20, 13, B(9), B(11), clk_tbl_sdc2_4, + NULL, CLK_HALT_STATEB_REG, 25, 0x4D7C), + CLK_MND8(SPI, SPI_NS_REG, 19, 12, B(9), B(11), clk_tbl_spi, NULL, + CLK_HALT_STATEC_REG, 0, 0x09), + CLK_MND8(MIDI, MIDI_NS_REG, 19, 12, B(9), B(11), clk_tbl_midi, NULL, + CLK_HALT_STATEC_REG, 1, 0x0A), + CLK_MND8_P(USB_HS_SRC, USBH_NS_REG, 23, 16, 0, B(11), clk_tbl_usb, + AXI_LI_ADSP_A, chld_usb_src, NULL, 0, 0), + CLK_SLAVE(USB_HS, USBH_NS_REG, B(9), USB_HS_SRC, + CLK_HALT_STATEB_REG, 26, 0x4D7F), + CLK_SLAVE(USB_HS_CORE, USBH_NS_REG, B(13), USB_HS_SRC, + CLK_HALT_STATEA_REG, 27, 0x14), + CLK_SLAVE(USB_HS2, USBH2_NS_REG, B(9), USB_HS_SRC, + CLK_HALT_STATEB_REG, 3, 0x4D73), + CLK_SLAVE(USB_HS2_CORE, USBH2_NS_REG, B(4), USB_HS_SRC, + CLK_HALT_STATEA_REG, 28, 0x15), + CLK_SLAVE(USB_HS3, USBH3_NS_REG, B(9), USB_HS_SRC, + CLK_HALT_STATEB_REG, 2, 0x4D74), + CLK_SLAVE(USB_HS3_CORE, USBH3_NS_REG, B(4), USB_HS_SRC, + CLK_HALT_STATEA_REG, 29, 0x16), + CLK_MND8(TV, TV_NS_REG, 23, 16, 0, B(11), clk_tbl_tv, chld_tv, + NULL, 0, 0), + CLK_SLAVE(HDMI, HDMI_NS_REG, B(9), TV, + CLK_HALT_STATEC_REG, 7, 0x13), + CLK_SLAVE(TV_DAC, TV_NS_REG, B(12), TV, + CLK_HALT_STATEB_REG, 27, 0x4D6C), + CLK_SLAVE(TV_ENC, TV_NS_REG, B(9), TV, + CLK_HALT_STATEB_REG, 10, 0x4D6B), + /* Hacking root & branch into one param. */ + CLK_SLAVE(TSIF_REF, TSIF_NS_REG, B(9)|B(11), TV, + CLK_HALT_STATEB_REG, 11, 0x4D6A), + + CLK_MND16(UART1DM, UART1DM_NS_REG, B(9), B(11), clk_tbl_uartdm, NONE, + NULL, CLK_HALT_STATEB_REG, 6, 0x4D70), + CLK_MND16(UART2DM, UART2DM_NS_REG, B(9), B(11), clk_tbl_uartdm, NONE, + NULL, CLK_HALT_STATEB_REG, 23, 0x4D7D), + CLK_MND16(JPEG, JPEG_NS_REG, B(9), B(11), clk_tbl_vfe_jpeg, AXI_LI_JPEG, + NULL, CLK_HALT_STATEB_REG, 1, 0x6000), + CLK_MND16(CAM_M, CAM_NS_REG, 0, B(9), clk_tbl_cam, NONE, NULL, + NULL, 0, 0x4D44), + CLK_MND16(VFE, CAM_VFE_NS_REG, B(9), B(13), clk_tbl_vfe_jpeg, + AXI_LI_VFE, chld_vfe, CLK_HALT_STATEB_REG, + 0, 0x4D76), + CLK_SLAVE(VFE_MDC, CAM_VFE_NS_REG, B(11), VFE, CLK_HALT_STATEA_REG, + 9, 0x4D57), + CLK_SLAVE(VFE_CAMIF, CAM_VFE_NS_REG, B(15), VFE, CLK_HALT_STATEC_REG, + 13, 0x7000), + CLK_SLAVE(CSI0_VFE, CSI_NS_REG, B(15), VFE, CLK_HALT_STATEC_REG, + 16, 0), + + CLK_MND16(SDAC, SDAC_NS_REG, B(9), B(11), clk_tbl_sdac, + NONE, chld_sdac, CLK_HALT_STATEA_REG, 2, 0x4D60), + CLK_SLAVE(SDAC_M, SDAC_NS_REG, B(12), SDAC, CLK_HALT_STATEB_REG, + 17, 0x4D66), + + CLK_MND16(MDP_LCDC_PCLK, MDP_LCDC_NS_REG, B(9), B(11), + clk_tbl_mdp_lcdc, NONE, chld_mdp_lcdc_p, + CLK_HALT_STATEB_REG, 28, 0x4200), + CLK_SLAVE(MDP_LCDC_PAD_PCLK, MDP_LCDC_NS_REG, B(12), MDP_LCDC_PCLK, + CLK_HALT_STATEB_REG, 29, 0x4100), + CLK_1RATE(MDP_VSYNC, MDP_VSYNC_REG, B(0), 0, clk_tbl_mdp_vsync, + CLK_HALT_STATEB_REG, 30, 0x4D53), + + CLK_MND16(MI2S_CODEC_RX_M, MI2S_RX_NS_REG, B(12), B(11), + clk_tbl_mi2s_codec, NONE, chld_mi2s_codec_rx, + CLK_HALT_STATEA_REG, 12, 0x4D4E), + CLK_SLAVE(MI2S_CODEC_RX_S, MI2S_RX_NS_REG, B(9), MI2S_CODEC_RX_M, + CLK_HALT_STATEA_REG, 13, 0x4D4F), + + CLK_MND16(MI2S_CODEC_TX_M, MI2S_TX_NS_REG, B(12), B(11), + clk_tbl_mi2s_codec, NONE, chld_mi2s_codec_tx, + CLK_HALT_STATEC_REG, 8, 0x4D50), + CLK_SLAVE(MI2S_CODEC_TX_S, MI2S_TX_NS_REG, B(9), MI2S_CODEC_TX_M, + CLK_HALT_STATEA_REG, 11, 0x17), + + CLK_MND16(MI2S_M, MI2S_NS_REG, B(12), B(11), + clk_tbl_mi2s, NONE, chld_mi2s, CLK_HALT_STATEC_REG, + 4, 0x0D), + CLK_SLAVE(MI2S_S, MI2S_NS_REG, B(9), MI2S_M, CLK_HALT_STATEC_REG, + 3, 0), + + CLK_LOCAL(GRP_2D, BASIC, 0, GRP_2D_NS_REG, F_MASK_BASIC | (7 << 12), + B(7), B(11), clk_tbl_grp, AXI_GRP_2D, NULL, + CLK_HALT_STATEA_REG, 31, 0x5C00), + CLK_LOCAL(GRP_3D_SRC, BASIC, 0, GRP_NS_REG, F_MASK_BASIC | (7 << 12), + 0, B(11), clk_tbl_grp, AXI_LI_GRP, chld_grp_3d_src, + 0, 0, 0), + CLK_SLAVE(GRP_3D, GRP_NS_REG, B(7), GRP_3D_SRC, CLK_HALT_STATEB_REG, + 18, 0x5E00), + CLK_SLAVE(IMEM, GRP_NS_REG, B(9), GRP_3D_SRC, CLK_HALT_STATEB_REG, + 19, 0x5F00), + CLK_LOCAL(LPA_CODEC, BASIC, 0, LPA_NS_REG, BM(1, 0), B(9), 0, + clk_tbl_lpa_codec, NONE, NULL, CLK_HALT_STATEC_REG, + 6, 0x0F), + + CLK_MND8(CSI0, CSI_NS_REG, 24, 17, B(9), B(11), clk_tbl_csi, NULL, + CLK_HALT_STATEC_REG, 17, 0x5F00), + + /* For global clocks to be on we must have GLBL_ROOT_ENA set */ + CLK_1RATE(GLBL_ROOT, GLBL_CLK_ENA_SC_REG, 0, B(29), clk_tbl_axi, + NULL, 0, 0), + + /* Peripheral bus clocks. */ + CLK_BRIDGE(ADM, GLBL_CLK_ENA_SC_REG, B(5), AXI_LI_APPS, + GLBL_CLK_STATE_REG, 5, 0x4000), + CLK_GLBL(ADM_P, GLBL_CLK_ENA_2_SC_REG, B(15), + GLBL_CLK_STATE_2_REG, 15, 0x11), + CLK_GLBL(CE, GLBL_CLK_ENA_SC_REG, B(6), + GLBL_CLK_STATE_REG, 6, 0x4D43), + CLK_GLBL(CAMIF_PAD_P, GLBL_CLK_ENA_SC_REG, B(9), + GLBL_CLK_STATE_REG, 9, 0x1A), + CLK_GLBL(CSI0_P, GLBL_CLK_ENA_SC_REG, B(30), + GLBL_CLK_STATE_REG, 30, 0), + CLK_GLBL(EMDH_P, GLBL_CLK_ENA_2_SC_REG, B(3), + GLBL_CLK_STATE_2_REG, 3, 0x03), + CLK_GLBL(GRP_2D_P, GLBL_CLK_ENA_SC_REG, B(24), + GLBL_CLK_STATE_REG, 24, 0x4D4C), + CLK_GLBL(GRP_3D_P, GLBL_CLK_ENA_2_SC_REG, B(17), + GLBL_CLK_STATE_2_REG, 17, 0x4D67), + CLK_GLBL(JPEG_P, GLBL_CLK_ENA_2_SC_REG, B(24), + GLBL_CLK_STATE_2_REG, 24, 0x4D5E), + CLK_GLBL(LPA_P, GLBL_CLK_ENA_2_SC_REG, B(7), + GLBL_CLK_STATE_2_REG, 7, 0x07), + CLK_GLBL(MDP_P, GLBL_CLK_ENA_2_SC_REG, B(6), + GLBL_CLK_STATE_2_REG, 6, 0x06), + CLK_GLBL(MFC_P, GLBL_CLK_ENA_2_SC_REG, B(26), + GLBL_CLK_STATE_2_REG, 26, 0x4D75), + CLK_GLBL(PMDH_P, GLBL_CLK_ENA_2_SC_REG, B(4), + GLBL_CLK_STATE_2_REG, 4, 0x04), + CLK_GLBL(ROTATOR_IMEM, GLBL_CLK_ENA_2_SC_REG, B(23), + GLBL_CLK_STATE_2_REG, 23, 0x6600), + CLK_GLBL(ROTATOR_P, GLBL_CLK_ENA_2_SC_REG, B(25), + GLBL_CLK_STATE_2_REG, 25, 0x4D6D), + CLK_GLBL(SDC1_P, GLBL_CLK_ENA_SC_REG, B(7), + GLBL_CLK_STATE_REG, 7, 0x4D61), + CLK_GLBL(SDC2_P, GLBL_CLK_ENA_SC_REG, B(8), + GLBL_CLK_STATE_REG, 8, 0x4F63), + CLK_GLBL(SDC3_P, GLBL_CLK_ENA_SC_REG, B(27), + GLBL_CLK_STATE_REG, 27, 0x4D79), + CLK_GLBL(SDC4_P, GLBL_CLK_ENA_SC_REG, B(28), + GLBL_CLK_STATE_REG, 28, 0x4D7B), + CLK_GLBL(SPI_P, GLBL_CLK_ENA_2_SC_REG, B(10), + GLBL_CLK_STATE_2_REG, 10, 0x18), + CLK_GLBL(TSIF_P, GLBL_CLK_ENA_SC_REG, B(18), + GLBL_CLK_STATE_REG, 18, 0x4D65), + CLK_GLBL(UART1DM_P, GLBL_CLK_ENA_SC_REG, B(17), + GLBL_CLK_STATE_REG, 17, 0x4D5C), + CLK_GLBL(UART2DM_P, GLBL_CLK_ENA_SC_REG, B(26), + GLBL_CLK_STATE_REG, 26, 0x4D7E), + CLK_GLBL(USB_HS2_P, GLBL_CLK_ENA_2_SC_REG, B(8), + GLBL_CLK_STATE_2_REG, 8, 0x08), + CLK_GLBL(USB_HS3_P, GLBL_CLK_ENA_2_SC_REG, B(9), + GLBL_CLK_STATE_2_REG, 9, 0x10), + CLK_GLBL(USB_HS_P, GLBL_CLK_ENA_SC_REG, B(25), + GLBL_CLK_STATE_REG, 25, 0x4D58), + CLK_GLBL(VFE_P, GLBL_CLK_ENA_2_SC_REG, B(27), + GLBL_CLK_STATE_2_REG, 27, 0x4D55), + + /* AXI bridge clocks. */ + CLK_BRIDGE(AXI_LI_APPS, GLBL_CLK_ENA_SC_REG, B(2), GLBL_ROOT, + GLBL_CLK_STATE_REG, 2, 0x4900), + CLK_BRIDGE(AXI_LI_ADSP_A, GLBL_CLK_ENA_2_SC_REG, B(14), AXI_LI_APPS, + GLBL_CLK_STATE_2_REG, 14, 0x6400), + CLK_BRIDGE(AXI_LI_JPEG, GLBL_CLK_ENA_2_SC_REG, B(19), AXI_LI_APPS, + GLBL_CLK_STATE_2_REG, 19, 0x4E00), + CLK_BRIDGE(AXI_LI_VFE, GLBL_CLK_ENA_SC_REG, B(23), AXI_LI_APPS, + GLBL_CLK_STATE_REG, 23, 0x5B00), + CLK_BRIDGE(AXI_MDP, GLBL_CLK_ENA_2_SC_REG, B(29), AXI_LI_APPS, + GLBL_CLK_STATE_2_REG, 29, 0x6B00), + + CLK_BRIDGE(AXI_IMEM, GLBL_CLK_ENA_2_SC_REG, B(18), GLBL_ROOT, + GLBL_CLK_STATE_2_REG, 18, 0x4B00), + + CLK_BRIDGE(AXI_LI_VG, GLBL_CLK_ENA_SC_REG, B(3), GLBL_ROOT, + GLBL_CLK_STATE_REG, 3, 0x4700), + CLK_BRIDGE(AXI_GRP_2D, GLBL_CLK_ENA_SC_REG, B(21), AXI_LI_VG, + GLBL_CLK_STATE_REG, 21, 0x5900), + CLK_BRIDGE(AXI_LI_GRP, GLBL_CLK_ENA_SC_REG, B(22), AXI_LI_VG, + GLBL_CLK_STATE_REG, 22, 0x5A00), + CLK_BRIDGE(AXI_MFC, GLBL_CLK_ENA_2_SC_REG, B(20), AXI_LI_VG, + GLBL_CLK_STATE_2_REG, 20, 0x6A00), + CLK_BRIDGE(AXI_ROTATOR, GLBL_CLK_ENA_2_SC_REG, B(22), AXI_LI_VG, + GLBL_CLK_STATE_2_REG, 22, 0x4300), + CLK_BRIDGE(AXI_VPE, GLBL_CLK_ENA_2_SC_REG, B(21), AXI_LI_VG, + GLBL_CLK_STATE_2_REG, 21, 0x6700), +}; + +static DEFINE_SPINLOCK(clock_reg_lock); +static DEFINE_SPINLOCK(pll_vote_lock); + +enum { + TCXO, + LPXO, + NUM_XO +}; +static unsigned xo_votes[NUM_XO]; /* Tracks the number of users for each XO */ + +/* Map PLLs to which XO they use */ +static const unsigned pll_to_xo[] = { + [PLL_0] = TCXO, + [PLL_1] = TCXO, + [PLL_2] = TCXO, + [PLL_3] = LPXO, + [PLL_4] = LPXO, + [PLL_5] = TCXO, + [PLL_6] = TCXO, +}; + +static void vote_for_xo(unsigned xo) +{ + BUG_ON(xo >= NUM_XO); + + if (!xo_votes[xo]) { + int enable = 1; + unsigned p_xo = xo; + msm_proc_comm(PCOM_CLKCTL_RPC_SRC_REQUEST, &p_xo, &enable); + } + xo_votes[xo]++; +} + +static void unvote_for_xo(unsigned xo) +{ + BUG_ON(xo >= NUM_XO); + + if (xo_votes[xo]) { + xo_votes[xo]--; + } else { + pr_warning("%s: Reference count mismatch!\n", __func__); + return; + } + + if (xo_votes[xo] == 0) { + int enable = 0; + msm_proc_comm(PCOM_CLKCTL_RPC_SRC_REQUEST, &xo, &enable); + } +} + +#define PLL_ACTIVE_MASK B(16) +void pll_enable(uint32_t pll) +{ + uint32_t reg_val; + unsigned long flags; + + BUG_ON(pll >= NUM_PLL); + + spin_lock_irqsave(&pll_vote_lock, flags); + if (!pll_count[pll]) { + vote_for_xo(pll_to_xo[pll]); + reg_val = readl(PLL_ENA_REG); + reg_val |= (1 << pll); + writel(reg_val, PLL_ENA_REG); + } + pll_count[pll]++; + spin_unlock_irqrestore(&pll_vote_lock, flags); + + /* Wait until PLL is enabled. */ + while ((readl(pll_status_addr[pll]) & PLL_ACTIVE_MASK) == 0) + cpu_relax(); +} + +void pll_disable(uint32_t pll) +{ + uint32_t reg_val; + unsigned long flags; + + BUG_ON(pll >= NUM_PLL); + + spin_lock_irqsave(&pll_vote_lock, flags); + if (pll_count[pll]) { + pll_count[pll]--; + } else { + pr_warning("Reference count mismatch in PLL disable!\n"); + goto out; + } + + if (pll_count[pll] == 0) { + reg_val = readl(PLL_ENA_REG); + reg_val &= ~(1 << pll); + writel(reg_val, PLL_ENA_REG); + unvote_for_xo(pll_to_xo[pll]); + } +out: + spin_unlock_irqrestore(&pll_vote_lock, flags); +} + +static void src_enable(uint32_t src) +{ + switch (src) { + case SRC_NONE: + /* + * SRC_NONE is used as a placeholder for some freqencies that + * don't have any direct PLL dependency. Instead they source + * off an external/internal clock which takes care of any + * PLL or XO dependency. + */ + break; + case SRC_TCXO: + vote_for_xo(TCXO); + break; + case SRC_AXI: + case SRC_LPXO: + /* + * AXI could use LPXO or TCXO. Map it to LPXO to make sure + * there is at least once XO available for the AXI (LPXO is + * the lower powered one so just use that). + */ + vote_for_xo(LPXO); + break; + default: + pll_enable(src_pll_tbl[src]); + break; + } +} + +static void src_disable(uint32_t src) +{ + switch (src) { + case SRC_NONE: + /* + * SRC_NONE is used as a placeholder for some freqencies that + * don't have any direct PLL dependency. Instead they source + * off an external/internal clock which takes care of any + * PLL or XO dependency. + */ + break; + case SRC_TCXO: + unvote_for_xo(TCXO); + break; + case SRC_AXI: + case SRC_LPXO: + /* + * AXI could use LPXO or TCXO. Map it to LPXO to make sure + * there is at least once XO available for the AXI (LPXO is + * the lower powered one so just use that). + */ + unvote_for_xo(LPXO); + break; + default: + pll_disable(src_pll_tbl[src]); + break; + } +} + +static unsigned msmc1_votes[MSMC1_END]; +static unsigned msmc1_level; + +static int update_msmc1(void) +{ + int err, target, mvolts; + + if (msmc1_votes[HIGH]) + target = 1200; + else if (msmc1_votes[NOMINAL]) + target = 1100; + else + target = 1000; + + if (target == msmc1_level) + return 0; + + mvolts = target; + err = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_MSMC1, &mvolts, NULL); + if (err) + goto out; + + if (mvolts) { + err = -EINVAL; + goto out; + } + msmc1_level = target; +out: + return err; +} + +static void unvote_msmc1(unsigned level) +{ + if (level >= ARRAY_SIZE(msmc1_votes)) + return; + + if (msmc1_votes[level]) { + msmc1_votes[level]--; + } else { + pr_warning("%s: Reference counts are incorrect\n", __func__); + return; + } + + update_msmc1(); +} + +static int vote_msmc1(unsigned level) +{ + int ret; + + if (level >= ARRAY_SIZE(msmc1_votes)) + return 0; + + msmc1_votes[level]++; + ret = update_msmc1(); + if (ret) + msmc1_votes[level]--; + + return ret; +} + +/* + * SoC specific register-based control of clocks. + */ +static int _soc_clk_enable(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + void *ns_reg = t->ns_reg; + uint32_t reg_val = 0; + + WARN((t->type != NORATE) && (t->current_freq == &dummy_freq), + "Attempting to enable clock %d before setting its rate. " + "Set the rate first!\n", id); + + reg_val = readl(ns_reg); + if (t->type == MND) { + /* mode can be either 0 or 1. So the R-value of the + * expression will evaluate to MNCNTR_EN_MASK or 0. This + * avoids the need for a "if(mode == 1)". A "&" will not work + * here. */ + reg_val |= (MNCNTR_EN_MASK * t->current_freq->mode); + writel(reg_val, ns_reg); + } + if (t->root_en_mask) { + reg_val |= t->root_en_mask; + writel(reg_val, ns_reg); + } + if (t->br_en_mask) { + reg_val |= t->br_en_mask; + writel(reg_val, ns_reg); + } + if (t->halt_reg) { + uint32_t halted, count = 0; + + /* Wait for the halt bit to clear, but timeout after 100usecs + * since the halt bit may be buggy. */ + while ((halted = readl(t->halt_reg) & BIT(t->halt_mask)) + && count++ < 100) + udelay(1); + if (halted) + pr_warning("%s: clock %d never turned on\n", __func__, + id); + } + return 0; +} + +static void _soc_clk_disable(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + void *ns_reg = t->ns_reg; + uint32_t reg_val = 0; + + reg_val = readl(ns_reg); + + if (t->br_en_mask) { + reg_val &= ~(t->br_en_mask); + writel(reg_val, ns_reg); + } + if (t->halt_reg) { + uint32_t halted, count = 0; + + /* Wait for the halt bit to be set, but timeout after 100usecs + * since the halt bit may be buggy. */ + while (!(halted = readl(t->halt_reg) & BIT(t->halt_mask)) + && count++ < 100) + udelay(1); + if (!halted) + pr_warning("%s: clock %d never turned off\n", __func__, + id); + } + if (t->root_en_mask) { + reg_val &= ~(t->root_en_mask); + writel(reg_val, ns_reg); + } + if (t->type == MND) { + reg_val &= ~MNCNTR_EN_MASK; + writel(reg_val, ns_reg); + } +} + +static int soc_clk_enable_nolock(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + int ret = 0; + + if (!t->count) { + ret = vote_msmc1(t->current_freq->msmc1); + if (ret) + return ret; + if (t->parent != C(NONE)) { + ret = soc_clk_enable_nolock(t->parent); + if (ret) + return ret; + } + src_enable(t->current_freq->src); + ret = _soc_clk_enable(id); + } + t->count++; + + return ret; +} + +static void soc_clk_disable_nolock(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + + if (!t->count) { + pr_warning("Reference count mismatch in clock disable!\n"); + return; + } + if (t->count) + t->count--; + if (t->count == 0) { + _soc_clk_disable(id); + src_disable(t->current_freq->src); + unvote_msmc1(t->current_freq->msmc1); + if (t->parent != C(NONE)) + soc_clk_disable_nolock(t->parent); + } + + return; +} + +static int update_pwr_rail(unsigned id, int enable) +{ + /* TODO: Implement internal power rail control */ + return 0; +} + +static int soc_clk_enable(unsigned id) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&clock_reg_lock, flags); + ret = soc_clk_enable_nolock(id); + if (ret) + goto unlock; + /* + * The modem might modify the register bits for the clock branch when + * the rail is enabled/disabled, so enable the rail inside the lock + * instead of outside it. + */ + ret = update_pwr_rail(id, 1); + if (ret) + soc_clk_disable_nolock(id); +unlock: + spin_unlock_irqrestore(&clock_reg_lock, flags); + + return ret; +} + +static void soc_clk_disable(unsigned id) +{ + unsigned long flags; + + spin_lock_irqsave(&clock_reg_lock, flags); + update_pwr_rail(id, 0); + soc_clk_disable_nolock(id); + spin_unlock_irqrestore(&clock_reg_lock, flags); +} + +static void soc_clk_auto_off(unsigned id) +{ + unsigned long flags; + + spin_lock_irqsave(&clock_reg_lock, flags); + _soc_clk_disable(id); + spin_unlock_irqrestore(&clock_reg_lock, flags); +} + +static long soc_clk_round_rate(unsigned id, unsigned rate) +{ + struct clk_local *t = &clk_local_tbl[id]; + const struct clk_freq_tbl *f; + + if (t->type != MND && t->type != BASIC) + return -EINVAL; + + for (f = t->freq_tbl; f->freq_hz != FREQ_END; f++) + if (f->freq_hz >= rate) + return f->freq_hz; + + return -EPERM; +} + +static int soc_clk_set_rate(unsigned id, unsigned rate) +{ + struct clk_local *t = &clk_local_tbl[id]; + const struct clk_freq_tbl *cf; + const struct clk_freq_tbl *nf; + uint32_t *chld = t->children; + void *ns_reg = t->ns_reg; + void *md_reg = t->md_reg; + uint32_t reg_val = 0; + int i, ret = 0; + unsigned long flags; + long rounded; + + rounded = soc_clk_round_rate(id, rate); + if (rounded != rate) + pr_warning("Use clk_round_rate() before clk_set_rate() with " + "clock %u\n", id); + rate = rounded; + + if (t->type != MND && t->type != BASIC) + return -EPERM; + + spin_lock_irqsave(&clock_reg_lock, flags); + cf = t->current_freq; + + if (rate == cf->freq_hz) + goto release_lock; + + for (nf = t->freq_tbl; nf->freq_hz != FREQ_END; nf++) + if (nf->freq_hz == rate) + break; + + if (nf->freq_hz == FREQ_END) { + ret = -EINVAL; + goto release_lock; + } + + if (t->freq_mask == 0) { + t->current_freq = nf; + goto release_lock; + } + + /* Disable all branches before changing rate to prevent jitter. */ + for (i = 0; chld && chld[i] != C(NONE); i++) { + struct clk_local *ch = &clk_local_tbl[chld[i]]; + /* Don't bother turning off if it is already off. + * Checking ch->count is cheaper (cache) than reading and + * writing to a register (uncached/unbuffered). */ + if (ch->count) { + reg_val = readl(ch->ns_reg); + reg_val &= ~(ch->br_en_mask); + writel(reg_val, ch->ns_reg); + } + } + + if (t->count) { + _soc_clk_disable(id); + + ret = vote_msmc1(nf->msmc1); + if (ret) + goto msmc1_err; + /* Turn on PLL of the new freq. */ + src_enable(nf->src); + } + + /* Some clocks share the same register, so must be careful when + * assuming a register doesn't need to be re-read. */ + reg_val = readl(ns_reg); + if (t->type == MND) { + reg_val |= MNCNTR_RST_MASK; + writel(reg_val, ns_reg); + /* TODO: Currently writing 0's into reserved bits for 8-bit + * MND. Can be avoided by adding md_mask. */ + if (nf->mode) + writel(nf->md_val, md_reg); + reg_val &= ~MNCNTR_MODE_MASK; + reg_val |= (MNCNTR_MODE * nf->mode); + } + reg_val &= ~(t->freq_mask); + reg_val |= nf->ns_val; + writel(reg_val, ns_reg); + + if (t->type == MND) { + reg_val &= ~MNCNTR_RST_MASK; + writel(reg_val, ns_reg); + } + + if (t->count) { + /* Turn off PLL of the old freq. */ + src_disable(cf->src); + unvote_msmc1(cf->msmc1); + } + + /* Current freq must be updated before _soc_clk_enable() is called to + * make sure the MNCNTR_E bit is set correctly. */ + t->current_freq = nf; + +msmc1_err: + if (t->count) + _soc_clk_enable(id); + /* Enable only branches that were ON before. */ + for (i = 0; chld && chld[i] != C(NONE); i++) { + struct clk_local *ch = &clk_local_tbl[chld[i]]; + if (ch->count) { + reg_val = readl(ch->ns_reg); + reg_val |= ch->br_en_mask; + writel(reg_val, ch->ns_reg); + } + } + +release_lock: + spin_unlock_irqrestore(&clock_reg_lock, flags); + return ret; +} + +static int soc_clk_set_min_rate(unsigned id, unsigned rate) +{ + long rounded = soc_clk_round_rate(id, rate); + return soc_clk_set_rate(id, rounded); +} + +static int soc_clk_set_max_rate(unsigned id, unsigned rate) +{ + return -EPERM; +} + +static int soc_clk_set_flags(unsigned id, unsigned clk_flags) +{ + uint32_t regval, ret = 0; + unsigned long flags; + + spin_lock_irqsave(&clock_reg_lock, flags); + switch (id) { + case C(VFE): + regval = readl(CAM_VFE_NS_REG); + /* Flag values chosen for backward compatibility + * with proc_comm remote clock control. */ + if (clk_flags == 0x00000100) { + /* Select external source. */ + regval |= B(14); + } else if (clk_flags == 0x00000200) { + /* Select internal source. */ + regval &= ~B(14); + } else + ret = -EINVAL; + + writel(regval, CAM_VFE_NS_REG); + break; + default: + ret = -EPERM; + } + spin_unlock_irqrestore(&clock_reg_lock, flags); + + return ret; +} + +static unsigned soc_clk_get_rate(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + unsigned long flags; + unsigned ret = 0; + + if (t->type == NORATE) + return 0; + + spin_lock_irqsave(&clock_reg_lock, flags); + ret = t->current_freq->freq_hz; + spin_unlock_irqrestore(&clock_reg_lock, flags); + + /* Return 0 if the rate has never been set. Might not be correct, + * but it's good enough. */ + if (ret == FREQ_END) + ret = 0; + + return ret; +} + +static unsigned soc_clk_is_enabled(unsigned id) +{ + return !!(clk_local_tbl[id].count); +} + + +struct clk_ops clk_ops_7x30 = { + .enable = soc_clk_enable, + .disable = soc_clk_disable, + .auto_off = soc_clk_auto_off, + .set_rate = soc_clk_set_rate, + .set_min_rate = soc_clk_set_min_rate, + .set_max_rate = soc_clk_set_max_rate, + .reset = pc_clk_reset, + .set_flags = soc_clk_set_flags, + .get_rate = soc_clk_get_rate, + .is_enabled = soc_clk_is_enabled, + .round_rate = soc_clk_round_rate, +}; + +/* + * Clock ownership detection code + */ + +enum { + SH2_OWN_GLBL, + SH2_OWN_APPS1, + SH2_OWN_APPS2, + SH2_OWN_ROW1, + SH2_OWN_ROW2, + SH2_OWN_APPS3, + NUM_OWNERSHIP +}; +static __initdata uint32_t ownership_regs[NUM_OWNERSHIP]; + +static void __init cache_ownership(void) +{ + ownership_regs[SH2_OWN_GLBL] = readl(SH2_OWN_GLBL_BASE_REG); + ownership_regs[SH2_OWN_APPS1] = readl(SH2_OWN_APPS1_BASE_REG); + ownership_regs[SH2_OWN_APPS2] = readl(SH2_OWN_APPS2_BASE_REG); + ownership_regs[SH2_OWN_ROW1] = readl(SH2_OWN_ROW1_BASE_REG); + ownership_regs[SH2_OWN_ROW2] = readl(SH2_OWN_ROW2_BASE_REG); + ownership_regs[SH2_OWN_APPS3] = readl(SH2_OWN_APPS3_BASE_REG); +} + +static void __init print_ownership(void) +{ + pr_info("Clock ownership\n"); + pr_info(" GLBL : %08x\n", ownership_regs[SH2_OWN_GLBL]); + pr_info(" APPS : %08x %08x %08x\n", ownership_regs[SH2_OWN_APPS1], + ownership_regs[SH2_OWN_APPS2], ownership_regs[SH2_OWN_APPS3]); + pr_info(" ROW : %08x %08x\n", ownership_regs[SH2_OWN_ROW1], + ownership_regs[SH2_OWN_ROW2]); +} + +/* + * This is a many-to-one mapping since we don't know how the remote clock code + * has decided to handle the dependencies between clocks for a particular + * hardware block. We determine the ownership for all the clocks on a block by + * checking the ownership bit of one register (usually the ns register). + */ +#define O(x) (&ownership_regs[(x)]) +static const struct clk_local_ownership { + const uint32_t *reg; + const uint32_t bit; +} ownership_map[] __initconst = { + [C(GRP_2D)] = { O(SH2_OWN_APPS1), B(6) }, + [C(GRP_2D_P)] = { O(SH2_OWN_APPS1), B(6) }, + [C(HDMI)] = { O(SH2_OWN_APPS1), B(31) }, + [C(JPEG)] = { O(SH2_OWN_APPS1), B(0) }, + [C(JPEG_P)] = { O(SH2_OWN_APPS1), B(0) }, + [C(LPA_CODEC)] = { O(SH2_OWN_APPS1), B(23) }, + [C(LPA_CORE)] = { O(SH2_OWN_APPS1), B(23) }, + [C(LPA_P)] = { O(SH2_OWN_APPS1), B(23) }, + [C(MI2S_M)] = { O(SH2_OWN_APPS1), B(28) }, + [C(MI2S_S)] = { O(SH2_OWN_APPS1), B(28) }, + [C(MI2S_CODEC_RX_M)] = { O(SH2_OWN_APPS1), B(12) }, + [C(MI2S_CODEC_RX_S)] = { O(SH2_OWN_APPS1), B(12) }, + [C(MI2S_CODEC_TX_M)] = { O(SH2_OWN_APPS1), B(14) }, + [C(MI2S_CODEC_TX_S)] = { O(SH2_OWN_APPS1), B(14) }, + [C(MIDI)] = { O(SH2_OWN_APPS1), B(22) }, + [C(SDAC)] = { O(SH2_OWN_APPS1), B(26) }, + [C(SDAC_M)] = { O(SH2_OWN_APPS1), B(26) }, + [C(VFE)] = { O(SH2_OWN_APPS1), B(8) }, + [C(VFE_CAMIF)] = { O(SH2_OWN_APPS1), B(8) }, + [C(VFE_MDC)] = { O(SH2_OWN_APPS1), B(8) }, + [C(VFE_P)] = { O(SH2_OWN_APPS1), B(8) }, + + [C(GRP_3D)] = { O(SH2_OWN_APPS2), B(0) }, + [C(GRP_3D_P)] = { O(SH2_OWN_APPS2), B(0) }, + [C(GRP_3D_SRC)] = { O(SH2_OWN_APPS2), B(0) }, + [C(IMEM)] = { O(SH2_OWN_APPS2), B(0) }, + [C(MDP_LCDC_PAD_PCLK)] = { O(SH2_OWN_APPS2), B(4) }, + [C(MDP_LCDC_PCLK)] = { O(SH2_OWN_APPS2), B(4) }, + [C(MDP_P)] = { O(SH2_OWN_APPS2), B(4) }, + [C(MDP_VSYNC)] = { O(SH2_OWN_APPS2), B(28) }, + [C(TSIF_REF)] = { O(SH2_OWN_APPS2), B(5) }, + [C(TSIF_P)] = { O(SH2_OWN_APPS2), B(5) }, + [C(TV)] = { O(SH2_OWN_APPS2), B(2) }, + [C(TV_DAC)] = { O(SH2_OWN_APPS2), B(2) }, + [C(TV_ENC)] = { O(SH2_OWN_APPS2), B(2) }, + + [C(EMDH)] = { O(SH2_OWN_ROW1), B(7) }, + [C(EMDH_P)] = { O(SH2_OWN_ROW1), B(7) }, + [C(I2C)] = { O(SH2_OWN_ROW1), B(11) }, + [C(I2C_2)] = { O(SH2_OWN_ROW1), B(12) }, + [C(MDC)] = { O(SH2_OWN_ROW1), B(17) }, + [C(PMDH)] = { O(SH2_OWN_ROW1), B(19) }, + [C(PMDH_P)] = { O(SH2_OWN_ROW1), B(19) }, + [C(SDC1)] = { O(SH2_OWN_ROW1), B(23) }, + [C(SDC1_P)] = { O(SH2_OWN_ROW1), B(23) }, + [C(SDC2)] = { O(SH2_OWN_ROW1), B(25) }, + [C(SDC2_P)] = { O(SH2_OWN_ROW1), B(25) }, + [C(SDC3)] = { O(SH2_OWN_ROW1), B(27) }, + [C(SDC3_P)] = { O(SH2_OWN_ROW1), B(27) }, + [C(SDC4)] = { O(SH2_OWN_ROW1), B(29) }, + [C(SDC4_P)] = { O(SH2_OWN_ROW1), B(29) }, + [C(UART2)] = { O(SH2_OWN_ROW1), B(0) }, + [C(USB_HS2)] = { O(SH2_OWN_ROW1), B(2) }, + [C(USB_HS2_CORE)] = { O(SH2_OWN_ROW1), B(2) }, + [C(USB_HS2_P)] = { O(SH2_OWN_ROW1), B(2) }, + [C(USB_HS3)] = { O(SH2_OWN_ROW1), B(4) }, + [C(USB_HS3_CORE)] = { O(SH2_OWN_ROW1), B(4) }, + [C(USB_HS3_P)] = { O(SH2_OWN_ROW1), B(4) }, + + [C(QUP_I2C)] = { O(SH2_OWN_ROW2), B(3) }, + [C(SPI)] = { O(SH2_OWN_ROW2), B(1) }, + [C(SPI_P)] = { O(SH2_OWN_ROW2), B(1) }, + [C(UART1)] = { O(SH2_OWN_ROW2), B(9) }, + [C(UART1DM)] = { O(SH2_OWN_ROW2), B(6) }, + [C(UART1DM_P)] = { O(SH2_OWN_ROW2), B(6) }, + [C(UART2DM)] = { O(SH2_OWN_ROW2), B(8) }, + [C(UART2DM_P)] = { O(SH2_OWN_ROW2), B(8) }, + [C(USB_HS)] = { O(SH2_OWN_ROW2), B(11) }, + [C(USB_HS_CORE)] = { O(SH2_OWN_ROW2), B(11) }, + [C(USB_HS_SRC)] = { O(SH2_OWN_ROW2), B(11) }, + [C(USB_HS_P)] = { O(SH2_OWN_ROW2), B(11) }, + + [C(CAM_M)] = { O(SH2_OWN_APPS3), B(6) }, + [C(CAMIF_PAD_P)] = { O(SH2_OWN_APPS3), B(6) }, + [C(CSI0)] = { O(SH2_OWN_APPS3), B(11) }, + [C(CSI0_VFE)] = { O(SH2_OWN_APPS3), B(11) }, + [C(CSI0_P)] = { O(SH2_OWN_APPS3), B(11) }, + [C(MDP)] = { O(SH2_OWN_APPS3), B(0) }, + [C(MFC)] = { O(SH2_OWN_APPS3), B(2) }, + [C(MFC_DIV2)] = { O(SH2_OWN_APPS3), B(2) }, + [C(MFC_P)] = { O(SH2_OWN_APPS3), B(2) }, + [C(VPE)] = { O(SH2_OWN_APPS3), B(4) }, + + [C(ADM)] = { O(SH2_OWN_GLBL), B(8) }, + [C(ADM_P)] = { O(SH2_OWN_GLBL), B(13) }, + [C(CE)] = { O(SH2_OWN_GLBL), B(8) }, + [C(AXI_ROTATOR)] = { O(SH2_OWN_GLBL), B(13) }, + [C(ROTATOR_IMEM)] = { O(SH2_OWN_GLBL), B(13) }, + [C(ROTATOR_P)] = { O(SH2_OWN_GLBL), B(13) }, +}; + +static bool __init clk_is_local(uint32_t id) +{ + uint32_t bit = ownership_map[id].bit; + const uint32_t *reg = ownership_map[id].reg; + + BUG_ON(id >= ARRAY_SIZE(ownership_map) || !reg); + + return *reg & bit; +} + +static const struct reg_init { + const void __iomem *reg; + uint32_t mask; + uint32_t val; +} ri_list[] __initconst = { + /* Enable UMDX_P clock. Known to causes issues, so never turn off. */ + {GLBL_CLK_ENA_2_SC_REG, B(2), B(2)}, + + {EMDH_NS_REG, BM(18, 17) , BVAL(18, 17, 0x3)}, /* RX div = div-4. */ + {PMDH_NS_REG, BM(18, 17), BVAL(18, 17, 0x3)}, /* RX div = div-4. */ + /* MI2S_CODEC_RX_S src = MI2S_CODEC_RX_M. */ + {MI2S_RX_NS_REG, B(14), 0x0}, + /* MI2S_CODEC_TX_S src = MI2S_CODEC_TX_M. */ + {MI2S_TX_NS_REG, B(14), 0x0}, + {MI2S_NS_REG, B(14), 0x0}, /* MI2S_S src = MI2S_M. */ + /* Allow DSP to decide the LPA CORE src. */ + {LPA_CORE_CLK_MA0_REG, B(0), B(0)}, + {LPA_CORE_CLK_MA2_REG, B(0), B(0)}, + {MI2S_CODEC_RX_DIV_REG, 0xF, 0xD}, /* MI2S_CODEC_RX_S div = div-8. */ + {MI2S_CODEC_TX_DIV_REG, 0xF, 0xD}, /* MI2S_CODEC_TX_S div = div-8. */ + {MI2S_DIV_REG, 0xF, 0x7}, /* MI2S_S div = div-8. */ + {MDC_NS_REG, 0x3, 0x3}, /* MDC src = external MDH src. */ + {SDAC_NS_REG, BM(15, 14), 0x0}, /* SDAC div = div-1. */ + /* Disable sources TCXO/5 & TCXO/6. UART1 src = TCXO*/ + {UART_NS_REG, BM(26, 25) | BM(2, 0), 0x0}, + {MDP_VSYNC_REG, 0xC, 0x4}, /* MDP VSYNC src = LPXO. */ + /* HDMI div = div-1, non-inverted. tv_enc_src = tv_clk_src */ + {HDMI_NS_REG, 0x7, 0x0}, + {TV_NS_REG, BM(15, 14), 0x0}, /* tv_clk_src_div2 = div-1 */ + + /* USBH core clocks src = USB_HS_SRC. */ + {USBH_NS_REG, B(15), B(15)}, + {USBH2_NS_REG, B(6), B(6)}, + {USBH3_NS_REG, B(6), B(6)}, +}; + +/* SoC-specific clk_ops initialization. */ +void __init msm_clk_soc_set_ops(struct clk *clk) +{ + if (!clk->ops) { + if (clk_is_local(clk->id)) + clk->ops = &clk_ops_7x30; + else { + clk->ops = &clk_ops_pcom; + clk->id = clk->remote_id; + } + } +} + +#define set_1rate(clk) \ + soc_clk_set_rate(C(clk), clk_local_tbl[C(clk)].freq_tbl->freq_hz) +void __init msm_clk_soc_init(void) +{ + int i; + uint32_t val; + + cache_ownership(); + print_ownership(); + + /* When we have no local clock control, the rest of the code in this + * function is a NOP since writes to shadow regions that we don't own + * are ignored. */ + + /* Disable all the child clocks of USB_HS_SRC. This needs to be done + * before the register init loop since it changes the source of the + * USB HS core clocks. */ + for (i = 0; chld_usb_src[i] != C(NONE); i++) + if (clk_is_local(chld_usb_src[i])) + _soc_clk_disable(chld_usb_src[i]); + + if (clk_is_local(C(USB_HS_SRC))) + soc_clk_set_rate(C(USB_HS_SRC), clk_tbl_usb[0].freq_hz); + + for (i = 0; i < ARRAY_SIZE(ri_list); i++) { + val = readl(ri_list[i].reg); + val &= ~ri_list[i].mask; + val |= ri_list[i].val; + writel(val, ri_list[i].reg); + } + + set_1rate(I2C); + set_1rate(I2C_2); + set_1rate(QUP_I2C); + set_1rate(UART1); + set_1rate(UART2); + set_1rate(MI2S_M); + set_1rate(MIDI); + set_1rate(MDP_VSYNC); + set_1rate(LPA_CODEC); + set_1rate(GLBL_ROOT); + + /* Sync the GRP2D clock to AXI */ + soc_clk_set_rate(C(GRP_2D), 1); +} diff --git a/arch/arm/mach-msm/clock-7x30.h b/arch/arm/mach-msm/clock-7x30.h index e16f72f..d41acb7 100644 --- a/arch/arm/mach-msm/clock-7x30.h +++ b/arch/arm/mach-msm/clock-7x30.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -33,6 +33,8 @@ enum { L_7X30_NONE_CLK = -1, L_7X30_ADM_CLK, + L_7X30_ADM_P_CLK, + L_7X30_CE_CLK, L_7X30_I2C_CLK, L_7X30_I2C_2_CLK, L_7X30_QUP_I2C_CLK, @@ -135,17 +137,30 @@ enum { L_7X30_NR_CLKS }; -struct clk_ops; -extern struct clk_ops clk_ops_7x30; - -struct clk_ops *clk_7x30_is_local(uint32_t id); -int clk_7x30_init(void); - void pll_enable(uint32_t pll); void pll_disable(uint32_t pll); +enum { + PLL_0 = 0, + PLL_1, + PLL_2, + PLL_3, + PLL_4, + PLL_5, + PLL_6, + NUM_PLL +}; + +enum { + LOW, + NOMINAL, + HIGH, + MSMC1_END +}; + extern int internal_pwr_rail_ctl_auto(unsigned rail_id, bool enable); +extern struct clk_ops clk_ops_7x30; #define CLK_7X30(clk_name, clk_id, clk_dev, clk_flags) { \ .name = clk_name, \ .id = L_7X30_##clk_id, \ @@ -164,5 +179,14 @@ extern int internal_pwr_rail_ctl_auto(unsigned rail_id, bool enable); .dbg_name = #l_id, \ } +#define CLK_7X30L(clk_name, l_id, clk_dev, clk_flags) { \ + .name = clk_name, \ + .id = L_7X30_##l_id, \ + .flags = clk_flags, \ + .dev = clk_dev, \ + .dbg_name = #l_id, \ + .ops = &clk_ops_7x30, \ + } + #endif diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 8c2b4dd..56c7549 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -162,21 +162,16 @@ EXPORT_SYMBOL(clk_set_flags); */ static struct clk *ebi1_clk; -static void __init set_clock_ops(struct clk *clk) -{ - if (!clk->ops) { - clk->ops = &clk_ops_pcom; - clk->id = clk->remote_id; - } -} - void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks) { unsigned n; + /* Do SoC-speficic clock init operations. */ + msm_clk_soc_init(); + mutex_lock(&clocks_mutex); for (n = 0; n < num_clocks; n++) { - set_clock_ops(&clock_tbl[n]); + msm_clk_soc_set_ops(&clock_tbl[n]); list_add_tail(&clock_tbl[n].list, &clocks); } mutex_unlock(&clocks_mutex); diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index 70216b0..4841ab6 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -62,6 +62,18 @@ struct clk { #define CLK_MAX CLKFLAG_MAX #define CLK_MINMAX (CLK_MIN | CLK_MAX) +#ifdef CONFIG_ARCH_MSM7X30 +void __init msm_clk_soc_set_ops(struct clk *clk); +#else +static inline void __init msm_clk_soc_set_ops(struct clk *clk) { } +#endif + +#if defined(CONFIG_ARCH_MSM7X30) +void __init msm_clk_soc_init(void); +#else +static inline void __init msm_clk_soc_init(void) { } +#endif + #ifdef CONFIG_DEBUG_FS int __init clock_debug_init(void); int __init clock_debug_add(struct clk *clock); diff --git a/arch/arm/mach-msm/proc_comm.h b/arch/arm/mach-msm/proc_comm.h index 12da4ca..e2f2d5a 100644 --- a/arch/arm/mach-msm/proc_comm.h +++ b/arch/arm/mach-msm/proc_comm.h @@ -137,6 +137,7 @@ enum { PCOM_CLKCTL_RPC_RAIL_DISABLE, PCOM_CLKCTL_RPC_RAIL_CONTROL, PCOM_CLKCTL_RPC_MIN_MSMC1, + PCOM_CLKCTL_RPC_SRC_REQUEST, PCOM_NUM_CMDS, }; -- Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html