/*
* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018, The Linux Foundation
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include "dsi_phy.h"
#include "dsi.xml.h"
#include "dsi_phy_7nm.xml.h"
/*
* DSI PLL 7nm - clock diagram (eg: DSI0): TODO: updated CPHY diagram
*
* dsi0_pll_out_div_clk dsi0_pll_bit_clk
* | |
* | |
* +---------+ | +----------+ | +----+
* dsi0vco_clk ---| out_div |--o--| divl_3_0 |--o--| /8 |-- dsi0_phy_pll_out_byteclk
* +---------+ | +----------+ | +----+
* | |
* | | dsi0_pll_by_2_bit_clk
* | | |
* | | +----+ | |\ dsi0_pclk_mux
* | |--| /2 |--o--| \ |
* | | +----+ | \ | +---------+
* | --------------| |--o--| div_7_4 |-- dsi0_phy_pll_out_dsiclk
* |------------------------------| / +---------+
* | +-----+ | /
* -----------| /4? |--o----------|/
* +-----+ | |
* | |dsiclk_sel
* |
* dsi0_pll_post_out_div_clk
*/
#define VCO_REF_CLK_RATE 19200000
#define FRAC_BITS 18
/* Hardware is pre V4.1 */
#define DSI_PHY_7NM_QUIRK_PRE_V4_1 BIT(0)
/* Hardware is V4.1 */
#define DSI_PHY_7NM_QUIRK_V4_1 BIT(1)
/* Hardware is V4.2 */
#define DSI_PHY_7NM_QUIRK_V4_2 BIT(2)
/* Hardware is V4.3 */
#define DSI_PHY_7NM_QUIRK_V4_3 BIT(3)
/* Hardware is V5.2 */
#define DSI_PHY_7NM_QUIRK_V5_2 BIT(4)
struct dsi_pll_config {
bool enable_ssc;
bool ssc_center;
u32 ssc_freq;
u32 ssc_offset;
u32 ssc_adj_per;
/* out */
u32 decimal_div_start;
u32 frac_div_start;
u32 pll_clock_inverters;
u32 ssc_stepsize;
u32 ssc_div_per;
};
struct pll_7nm_cached_state {
unsigned long vco_rate;
u8 bit_clk_div;
u8 pix_clk_div;
u8 pll_out_div;
u8 pll_mux;
};
struct dsi_pll_7nm {
struct clk_hw clk_hw;
struct msm_dsi_phy *phy;
u64 vco_current_rate;
/* protects REG_DSI_7nm_PHY_CMN_CLK_CFG0 register */
spinlock_t postdiv_lock;
struct pll_7nm_cached_state cached_state;
struct dsi_pll_7nm *slave;
};
#define to_pll_7nm(x) container_of(x, struct dsi_pll_7nm, clk_hw)
/*
* Global list of private DSI PLL struct pointers. We need this for bonded DSI
* mode, where the master PLL's clk_ops needs access the slave's private data
*/
static struct dsi_pll_7nm *pll_7nm_list[DSI_MAX];
static void dsi_pll_setup_config(struct dsi_pll_config *config)
{
config->ssc_freq = 31500;
config->ssc_offset = 4800;
config->ssc_adj_per = 2;
/* TODO: ssc enable */
config->enable_ssc = false;
config->ssc_center = 0;
}
static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll, struct dsi_pll_config *config)
{
u64 fref = VCO_REF_CLK_RATE;
u64 pll_freq;
u64 divider;
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
pll_freq = pll->vco_current_rate;
divider = fref * 2;
multiplier = 1 << FRAC_BITS;
dec_multiple = div_u64(pll_freq * multiplier, divider);
dec = div_u64_rem(dec_multiple, multiplier, &frac);
if (pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1)
config->pll_clock_inverters = 0x28;
else if ((pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2)) {
if (pll_freq <= 1300000000ULL)
config->pll_clock_inverters = 0xa0