blob: e95fe9137fbfbaffaa452678666a74ea8319dcd3 [file] [log] [blame]
/*
* Copyright (c) 2012 - 2014 The Linux Foundation. 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.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch-ipq806x/clk.h>
#define MSM_CLK_CTL_BASE 0x00900000
#define GSBIn_UART_APPS_MD_REG(n) (MSM_CLK_CTL_BASE + 0x29D0 + (0x20*((n)-1)))
#define GSBIn_UART_APPS_NS_REG(n) (MSM_CLK_CTL_BASE + 0x29D4 + (0x20*((n)-1)))
#define GSBIn_HCLK_CTL_REG(n) (MSM_CLK_CTL_BASE + 0x29C0 + (0x20*((n)-1)))
#define BB_PLL_ENA_SC0_REG (MSM_CLK_CTL_BASE + 0x34C0)
#define PLL_LOCK_DET_STATUS_REG (MSM_CLK_CTL_BASE + 0x03420)
#define MN_MODE_DUAL_EDGE 0x2
#define BM(m, l) (((((unsigned int)-1) << (31-m)) >> (31-m+l)) << l)
#define BVAL(m, l, val) (((val) << l) & BM(m, l))
#define Uart_clk_ns_mask (BM(31, 16) | BM(6, 0))
#define Uart_en_mask BIT(11)
#define MD16(m, n) (BVAL(31, 16, m) | BVAL(15, 0, ~(n)))
/* NS Registers */
#define NS(n_msb, n_lsb, n, m, mde_lsb, d_msb, d_lsb, d, s_msb, s_lsb, s) \
(BVAL(n_msb, n_lsb, ~(n-m)) \
| (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) \
| BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s))
#ifdef CONFIG_IPQ806X_I2C
unsigned int gsbi_port = 4;
unsigned int GSBI_I2C_CLK_M = 1;
unsigned int GSBI_I2C_CLK_N = 4;
unsigned int GSBI_I2C_CLK_D = 2;
#endif
/**
* uart_pll_vote_clk_enable - enables PLL8
*/
void uart_pll_vote_clk_enable(void)
{
setbits_le32(BB_PLL_ENA_SC0_REG, BIT(8));
while((readl(PLL_LOCK_DET_STATUS_REG) & BIT(8)) == 0);
}
#ifdef CONFIG_USB_XHCI_IPQ
/**
* usb_pll_vote_clk_enable - enables PLL8
*/
void usb_pll_vote_clk_enable(void)
{
setbits_le32(BB_PLL_ENA_SC0_REG, BIT(0));
while((readl(PLL_LOCK_DET_STATUS_REG) & BIT(0)) == 0);
}
#endif
/**
* uart_set_rate_mnd - configures divider M and D values
*
* Sets the M, D parameters of the divider to generate the GSBI UART
* apps clock.
*/
static void uart_set_rate_mnd(unsigned int gsbi_port, unsigned int m,
unsigned int n)
{
/* Assert MND reset. */
setbits_le32(GSBIn_UART_APPS_NS_REG(gsbi_port), BIT(7));
/* Program M and D values. */
writel(MD16(m, n), GSBIn_UART_APPS_MD_REG(gsbi_port));
/* Deassert MND reset. */
clrbits_le32(GSBIn_UART_APPS_NS_REG(gsbi_port), BIT(7));
}
#ifdef CONFIG_USB_XHCI_IPQ
/**
* usb_set_rate_mnd - configures divider M and D values
*
* Sets the M, D parameters of the divider to generate the USB
* apps clock.
*/
static void usb_set_rate_mnd(unsigned int usb_port, unsigned int m,
unsigned int n)
{
/* Assert MND reset. */
setbits_le32(USB30_MASTER_CLK_NS, BIT(7));
/* Program M and D values. */
writel(MD8(16, m, 0, n), USB30_MASTER_CLK_MD);
/* Deassert MND reset. */
clrbits_le32(USB30_MASTER_CLK_NS, BIT(7));
}
static void usb_set_rate_mnd_utmi(unsigned int usb_port, unsigned int m,
unsigned int n)
{
/* Assert MND reset. */
setbits_le32(USB30_MOC_UTMI_CLK_NS, BIT(7));
/* Program M and D values. */
writel(MD8(16, m, 0, n), USB30_MOC_UTMI_CLK_MD);
/* Deassert MND reset. */
clrbits_le32(USB30_MOC_UTMI_CLK_NS, BIT(7));
}
#endif
/**
* uart_branch_clk_enable_reg - enables branch clock
*
* Enables branch clock for GSBI UART port.
*/
static void uart_branch_clk_enable_reg(unsigned int gsbi_port)
{
setbits_le32(GSBIn_UART_APPS_NS_REG(gsbi_port), BIT(9));
}
#ifdef CONFIG_USB_XHCI_IPQ
/**
* usb_local_clock_enable - configures N value and enables root clocks
*
* Sets the N parameter of the divider and enables root clock and
* branch clocks for USB port.
*/
static void usb_utmi_local_clock_enable(unsigned int usb_port, unsigned int n,
unsigned int m)
{
unsigned int reg_val, usb_ns_val;
void *const reg = (void *)USB30_MOC_UTMI_CLK_NS;
/*
* Program the NS register, if applicable. NS registers are not
* set in the set_rate path because power can be saved by deferring
* the selection of a clocked source until the clock is enabled.
*/
reg_val = readl(reg);
reg_val &= ~(USB_clk_ns_mask);
usb_ns_val = NS(23,16,n,m, 5, 4, 3, 1, 2, 0,3);
reg_val |= (usb_ns_val & USB_clk_ns_mask);
writel(reg_val,reg);
/* enable MNCNTR_EN */
reg_val = readl(reg);
reg_val |= BIT(8);
writel(reg_val, reg);
/* set source to PLL8 running @384MHz */
reg_val = readl(reg);
reg_val |= 0x2;
writel(reg_val, reg);
/* Enable root. */
reg_val = readl(reg);
reg_val |= USB_en_mask;
writel(reg_val, reg);
}
/**
* usb_local_clock_enable - configures N value and enables root clocks
*
* Sets the N parameter of the divider and enables root clock and
* branch clocks for USB port.
*/
static void usb_local_clock_enable(unsigned int usb_port, unsigned int n,
unsigned int m)
{
unsigned int reg_val, usb_ns_val;
void *const reg = (void *)USB30_MASTER_CLK_NS;
/*
* Program the NS register, if applicable. NS registers are not
* set in the set_rate path because power can be saved by deferring
* the selection of a clocked source until the clock is enabled.
*/
reg_val = readl(reg);
reg_val &= ~(USB_clk_ns_mask);
usb_ns_val = NS(23,16,n,m, 5, 4, 3, 1, 2, 0,3);
reg_val |= (usb_ns_val & USB_clk_ns_mask);
writel(reg_val,reg);
/* enable MNCNTR_EN */
reg_val = readl(reg);
reg_val |= BIT(8);
writel(reg_val, reg);
/* set source to PLL8 running @384MHz */
reg_val = readl(reg);
reg_val |= 0x2;
writel(reg_val, reg);
/* Enable root. */
reg_val = readl(reg);
reg_val |= USB_en_mask;
writel(reg_val, reg);
}
/*
* usb_set_master_clk - enables MASTER CLK for USB port
*/
static void usb_set_master_clk(unsigned int usb_port)
{
setbits_le32(USB30_MASTER_CLK_CTL, BIT(4));
}
/**
* usb_set_master_1_clk - enables MASTER_1 CLK for USB port
*/
static void usb_set_master_1_clk(unsigned int usb_port)
{
setbits_le32(USB30_MASTER_1_CLK_CTL, BIT(4));
}
/**
* usb_set_utmi_clk - enables utmi branch port
*/
static void usb_set_utmi_clk(unsigned int usb_port)
{
setbits_le32(USB30_MOC_UTMI_CLK_CTL, BIT(4));
}
/**
* usb_set_utmi_1_clk - enables utmi branch port
*/
static void usb_set_utmi_1_clk(unsigned int usb_port)
{
setbits_le32(USB30_MOC_1_UTMI_CLK_CTL, BIT(4));
}
#endif
/**
* uart_local_clock_enable - configures N value and enables root clocks
*
* Sets the N parameter of the divider and enables root clock and
* branch clocks for GSBI UART port.
*/
static void uart_local_clock_enable(unsigned int gsbi_port, unsigned int n,
unsigned int m)
{
unsigned int reg_val, uart_ns_val;
void *const reg = (void *)GSBIn_UART_APPS_NS_REG(gsbi_port);
/*
* Program the NS register, if applicable. NS registers are not
* set in the set_rate path because power can be saved by deferring
* the selection of a clocked source until the clock is enabled.
*/
reg_val = readl(reg); // REG(0x29D4+(0x20*((n)-1)))
reg_val &= ~(Uart_clk_ns_mask);
uart_ns_val = NS(31,16,n,m, 5, 4, 3, 1, 2, 0,3);
reg_val |= (uart_ns_val & Uart_clk_ns_mask);
writel(reg_val,reg);
/* enable MNCNTR_EN */
reg_val = readl(reg);
reg_val |= BIT(8);
writel(reg_val, reg);
/* set source to PLL8 running @384MHz */
reg_val = readl(reg);
reg_val |= 0x3;
writel(reg_val, reg);
/* Enable root. */
reg_val |= Uart_en_mask;
writel(reg_val, reg);
uart_branch_clk_enable_reg(gsbi_port);
}
/**
* uart_set_gsbi_clk - enables HCLK for UART GSBI port
*/
static void uart_set_gsbi_clk(unsigned int gsbi_port)
{
setbits_le32(GSBIn_HCLK_CTL_REG(gsbi_port), BIT(4));
}
#ifdef CONFIG_USB_XHCI_IPQ
/**
*
* USB_clock_config - configures USB3.0 clocks
*
* Configures USB dividers, enable root and branch clocks.
*/
void usb_ss_core_clock_config(unsigned int usb_port, unsigned int m,
unsigned int n, unsigned int d)
{
usb_set_rate_mnd(usb_port, m, n);
usb_pll_vote_clk_enable();
usb_local_clock_enable(usb_port, n, m);
usb_set_master_clk(usb_port);
usb_set_master_1_clk(usb_port);
}
void usb_ss_utmi_clock_config(unsigned int usb_port, unsigned int m,
unsigned int n, unsigned int d)
{
usb_set_rate_mnd_utmi(usb_port, m, n);
usb_utmi_local_clock_enable(usb_port, n, m);
usb_set_utmi_clk(usb_port);
usb_set_utmi_1_clk(usb_port);
}
#endif
/**
* uart_clock_config - configures UART clocks
*
* Configures GSBI UART dividers, enable root and branch clocks.
*/
void uart_clock_config(unsigned int gsbi_port, unsigned int m,
unsigned int n, unsigned int d)
{
uart_set_rate_mnd(gsbi_port, m, d);
uart_pll_vote_clk_enable();
uart_local_clock_enable(gsbi_port, n, m);
uart_set_gsbi_clk(gsbi_port);
}
#ifdef CONFIG_IPQ806X_I2C
/**
* i2c_set_rate_mnd - configures divider M and D values
*
* Sets the M, D parameters of the divider to generate the GSBI QUP
* apps clock.
*/
static void i2c_set_rate_mnd(void)
{
/* Assert MND reset. */
setbits_le32(GSBIn_QUP_APPS_NS_REG(gsbi_port), BIT(7));
/* Program M and D values. */
writel(MD16(GSBI_I2C_CLK_M, GSBI_I2C_CLK_D), GSBIn_QUP_APPS_MD_REG(gsbi_port));
/* Deassert MND reset. */
clrbits_le32(GSBIn_QUP_APPS_NS_REG(gsbi_port), BIT(7));
}
/**
* i2c_pll_vote_clk_enable - enables PLL8
*/
void i2c_pll_vote_clk_enable(void)
{
setbits_le32(BB_PLL_ENA_SC0_REG, BIT(8));
while((readl(PLL_LOCK_DET_STATUS_REG) & BIT(8)) == 0);
}
/**
* i2c_branch_clk_enable_reg - enables branch clock
*
* Enables branch clock for GSBI I2C port.
*/
static void i2c_branch_clk_enable_reg(void)
{
setbits_le32(GSBIn_QUP_APPS_NS_REG(gsbi_port), BIT(9));
}
/**
* i2c_local_clock_enable - configures N value and enables root clocks
*
* Sets the N parameter of the divider and enables root clock and
* branch clocks for GSBI I2C port.
*/
static void i2c_local_clock_enable(void)
{
unsigned int reg_val, i2c_ns_val;
void *const reg = (void *)GSBIn_QUP_APPS_NS_REG(gsbi_port);
/*
* Program the NS register, if applicable. NS registers are not
* set in the set_rate path because power can be saved by deferring
* the selection of a clocked source until the clock is enabled.
*/
reg_val = readl(reg);
reg_val &= ~(I2C_clk_ns_mask);
i2c_ns_val = NS(23,16,GSBI_I2C_CLK_N, GSBI_I2C_CLK_M, 5, 4, 3, 4, 2, 0, 3);
reg_val |= (i2c_ns_val & I2C_clk_ns_mask);
writel(reg_val,reg);
/* enable MNCNTR_EN */
reg_val = readl(reg);
reg_val |= BIT(8);
writel(reg_val, reg);
/* set source to PLL8 running @384MHz */
reg_val = readl(reg);
reg_val |= 0x3;
writel(reg_val, reg);
/* Enable root. */
reg_val |= I2C_en_mask;
writel(reg_val, reg);
i2c_branch_clk_enable_reg();
}
/**
* i2c_set_gsbi_clk - enables HCLK for I2C GSBI port
*/
static void i2c_set_gsbi_clk(void)
{
setbits_le32(GSBIn_HCLK_CTL_REG(gsbi_port), BIT(4));
}
/**
* i2c_clock_config - configures I2C clocks
*
* Configures GSBI I2C dividers, enable root and branch clocks.
*/
void i2c_clock_config(void)
{
i2c_set_rate_mnd();
i2c_pll_vote_clk_enable();
i2c_local_clock_enable();
i2c_set_gsbi_clk();
}
#endif
#ifdef CONFIG_IPQ_NAND
/**
* nand_clock_config - configure NAND controller clocks
*
* Enable clocks to EBI2. Must be invoked before touching EBI2
* registers.
*/
void nand_clock_config(void)
{
writel(CLK_BRANCH_ENA(1) | ALWAYS_ON_CLK_BRANCH_ENA(1),
EBI2_CLK_CTL_REG);
/* Wait for clock to stabilize. */
udelay(10);
}
#endif
void pcie_clock_shutdown(pci_clk_offset_t *pci_clk)
{
/* PCIE_ALT_REF_CLK_NS */
writel(0x0, pci_clk->alt_ref_clk_ns);
/* PCIE20_PARF_PHY_REFCLK */
writel(0x1019, pci_clk->parf_phy_refclk);
/* PCIE_ACLK_CTL */
writel(0x0, pci_clk->aclk_ctl);
/* PCIE_PCLK_CTL */
writel(0x0, pci_clk->pclk_ctl);
/* PCIE_HCLK_CTL */
writel(0x0, pci_clk->hclk_ctl);
/* PCIE_AUX_CLK_CTL */
writel(0x0, pci_clk->aux_clk_ctl);
}
void pcie_clock_config(pci_clk_offset_t *pci_clk)
{
/* PCIE_ALT_REF_CLK_NS */
writel(0x0A59, pci_clk->alt_ref_clk_ns);
/* PCIE_ACLK_FS */
writel(0x4F, pci_clk->aclk_fs);
/* PCIE_PCLK_FS */
writel(0x4F, pci_clk->pclk_fs);
/* PCIE_ACLK_CTL */
writel(0x10, pci_clk->aclk_ctl);
/* PCIE_PCLK_CTL */
writel(0x10, pci_clk->pclk_ctl);
/* PCIE_HCLK_CTL */
writel(0x10, pci_clk->hclk_ctl);
/* PCIE_AUX_CLK_CTL */
writel(0x10, pci_clk->aux_clk_ctl);
}
#ifdef CONFIG_QCA_MMC
void emmc_pll_vote_clk_enable(void)
{
setbits_le32(BB_PLL_ENA_SC0_REG, BIT(8));
while((readl(PLL_LOCK_DET_STATUS_REG) & BIT(8)) == 0);
}
static void emmc_set_rate_mnd( unsigned int m, unsigned int n)
{
/* Assert MND reset. */
setbits_le32(SDC1_APPS_CLK_NS, BIT(7));
/* Program M and D values. */
writel(MD8(16, m, 0, n), SDC1_APPS_CLK_MD);
/* Deassert MND reset. */
clrbits_le32(SDC1_APPS_CLK_NS, BIT(7));
}
static void emmc_set_iface_clk(void)
{
setbits_le32(SDC1_HCLK_CTL, BIT(4));
}
static void emmc_local_clock_enable(unsigned int m, unsigned int n,
unsigned int d, unsigned int mux, unsigned int pll)
{
unsigned int reg_val, emmc_ns_val;
void *const reg = (void *)SDC1_APPS_CLK_NS;
/*
* Program the NS register, if applicable. NS registers are not
* set in the set_rate path because power can be saved by deferring
* the selection of a clocked source until the clock is enabled.
*/
reg_val = readl(reg);
reg_val &= ~(emmc_clk_ns_mask);
emmc_ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, mux);
reg_val |= (emmc_ns_val & emmc_clk_ns_mask);
writel(reg_val,reg);
reg_val = readl(reg);
reg_val |= pll;
writel(reg_val, reg);
/* Enable MNCNTR_EN */
reg_val = readl(reg);
reg_val |= BIT(8);
writel(reg_val, reg);
/* Enable root. */
reg_val = readl(reg);
reg_val |= emmc_en_mask;
writel(reg_val, reg);
/* Enable branch*/
reg_val = readl(reg);
reg_val |= BIT(9);
writel(reg_val, reg);
}
void emmc_clock_disable(void)
{
unsigned int reg_val, emmc_ns_val;
void *const reg = (void *)SDC1_APPS_CLK_NS;
emmc_ns_val = NS(23, 16, 240, 1, 5, 4, 3, 4, 2, 0, 3);
/* Assert MND reset. */
setbits_le32(SDC1_APPS_CLK_NS, BIT(7));
/* Program M and D values. */
writel(0, SDC1_APPS_CLK_MD);
/* Deassert MND reset. */
clrbits_le32(SDC1_APPS_CLK_NS, BIT(7));
/* Disable branch*/
reg_val = readl(reg);
reg_val &= ~BIT(9);
writel(reg_val, reg);
/* Disable root. */
reg_val = readl(reg);
reg_val &= ~emmc_en_mask;
writel(reg_val, reg);
/* Disable MNCNTR_EN */
reg_val = readl(reg);
reg_val &= ~BIT(8);
writel(reg_val, reg);
reg_val = readl(reg);
reg_val &= ~(emmc_clk_ns_mask);
reg_val |= emmc_ns_val & emmc_clk_ns_mask;
writel(reg_val, reg);
}
void emmc_clock_reset(void)
{
/*APPS CLK Assert*/
writel(0x1, SDC1_RESET);
/*APPS CLK Dessert*/
writel(0x0, SDC1_RESET);
}
void emmc_clock_config(int mode)
{
emmc_clock_disable();
udelay(10);
emmc_set_iface_clk();
if (mode == MMC_IDENTIFY_MODE) {
/*400 KHz pll8*/
emmc_set_rate_mnd(1, 240);
emmc_pll_vote_clk_enable();
emmc_local_clock_enable(1, 240, 4, 3, 3);
emmc_clock_reset();
udelay(10);
}
if (mode == MMC_DATA_TRANSFER_MODE) {
/*48 MHz pll8 */
emmc_set_rate_mnd(1, 8);
emmc_pll_vote_clk_enable();
emmc_local_clock_enable(1, 8, 1, 3, 3);
emmc_clock_reset();
udelay(10);
}
}
#endif