320 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * Copyright (c) 2024 Rockchip Electronics Co., Ltd.
 | 
						|
 * Author: Jason Zhang <jason.zhang@rock-chips.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include "it6621.h"
 | 
						|
#include "it6621-clk.h"
 | 
						|
#include "it6621-earc.h"
 | 
						|
#include "it6621-reg-bank0.h"
 | 
						|
#include "it6621-reg-bank1.h"
 | 
						|
 | 
						|
static int it6621_get_100ms_cnt(struct it6621_priv *priv, u32 *count)
 | 
						|
{
 | 
						|
	unsigned int val;
 | 
						|
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_GEN0, IT6621_100MS_CNT_EN);
 | 
						|
	msleep(99);
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_GEN0, IT6621_100MS_CNT_DIS);
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_100MS_CNT_7_0, &val);
 | 
						|
	*count = (u32)val;
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_100MS_CNT_15_8, &val);
 | 
						|
	*count += ((u32)(val) << 8);
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_100MS_CNT_23_16, &val);
 | 
						|
	*count += ((u32)(val) << 16);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int it6621_get_lcf(struct it6621_priv *priv, unsigned int *lcf)
 | 
						|
{
 | 
						|
	unsigned int timer_1us_flt;
 | 
						|
	unsigned int lcf_high;
 | 
						|
	unsigned int lcf_low;
 | 
						|
	unsigned int tmp;
 | 
						|
 | 
						|
	if (priv->fixed_lcf) {
 | 
						|
		*lcf = priv->fixed_lcf;
 | 
						|
		dev_info(priv->dev, "lcf: %d\n", *lcf);
 | 
						|
	} else {
 | 
						|
		regmap_update_bits(priv->regmap, 0xa9, 0x0f, 0x00);
 | 
						|
		it6621_get_100ms_cnt(priv, &lcf_low);
 | 
						|
 | 
						|
		regmap_update_bits(priv->regmap, 0xa9, 0x0f, 0x0f);
 | 
						|
		it6621_get_100ms_cnt(priv, &lcf_high);
 | 
						|
 | 
						|
		if (!(lcf_high - lcf_low)) {
 | 
						|
			dev_err(priv->dev, "Invalid LCF");
 | 
						|
			return -EINVAL;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Get tenths */
 | 
						|
		tmp = (1000000 - lcf_low) * 16 * 10 / (lcf_high - lcf_low);
 | 
						|
		timer_1us_flt = tmp % 10;
 | 
						|
		tmp = tmp / 10;
 | 
						|
		if (timer_1us_flt >= 5)
 | 
						|
			*lcf = tmp + 1;
 | 
						|
		else
 | 
						|
			*lcf = tmp;
 | 
						|
 | 
						|
		dev_info(priv->dev, "lcf low: %d, lcf high: %d, lcf: %d\n",
 | 
						|
			 lcf_low, lcf_high, *lcf);
 | 
						|
	}
 | 
						|
 | 
						|
	if (*lcf > 15)
 | 
						|
		*lcf = 15;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int it6621_get_refclk(struct it6621_priv *priv, unsigned int *refclk)
 | 
						|
{
 | 
						|
	if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK)
 | 
						|
		*refclk = priv->rclk / 2;
 | 
						|
	else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_2)
 | 
						|
		*refclk = priv->rclk;
 | 
						|
	else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_4)
 | 
						|
		*refclk = priv->rclk * 2;
 | 
						|
	else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_8)
 | 
						|
		*refclk = priv->rclk * 4;
 | 
						|
	else
 | 
						|
		*refclk = 0;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int it6621_calc_rclk(struct it6621_priv *priv)
 | 
						|
{
 | 
						|
	unsigned int timer_1us;
 | 
						|
	unsigned int timer_1us_int;
 | 
						|
	unsigned int timer_1us_flt;
 | 
						|
	unsigned int lcf;
 | 
						|
	unsigned int aclk_bnd_num;
 | 
						|
	unsigned int aclk_valid_num;
 | 
						|
	unsigned int sum;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = it6621_get_lcf(priv, &lcf);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	regmap_update_bits(priv->regmap, 0xa9, 0x0f, lcf);
 | 
						|
 | 
						|
	it6621_get_100ms_cnt(priv, &sum);
 | 
						|
	priv->rclk = sum / 100;
 | 
						|
	dev_dbg(priv->dev, "RCLK: %uMHz\n", priv->rclk / 1000);
 | 
						|
 | 
						|
	/* Update 1us timer */
 | 
						|
	timer_1us = sum / 100000;
 | 
						|
	timer_1us_int = timer_1us;
 | 
						|
	timer_1us_flt = sum % 100000;
 | 
						|
	timer_1us_flt <<= 8;
 | 
						|
	timer_1us_flt /= 100000;
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_GEN4, timer_1us_int);
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_1US_TIME_FLT, timer_1us_flt);
 | 
						|
 | 
						|
	if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK)
 | 
						|
		sum /= 2;
 | 
						|
	else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_4)
 | 
						|
		sum *= 2;
 | 
						|
	else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_8)
 | 
						|
		sum *= 4;
 | 
						|
 | 
						|
	aclk_bnd_num = sum * 128 / 358400;
 | 
						|
	aclk_valid_num = sum * 128 / 200000;
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_CLK_DET0, aclk_bnd_num & 0xff);
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_CLK_DET1,
 | 
						|
		     (aclk_bnd_num & 0x100) >> 8);
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_CLK_DET2,
 | 
						|
		     aclk_valid_num & 0xff);
 | 
						|
	regmap_write(priv->regmap, IT6621_REG_CLK_DET3,
 | 
						|
		     (aclk_valid_num & 0x300) >> 8);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * it6621_force_pdiv() - If auto mode error , FW force to correct PLL's setting
 | 
						|
 * @priv: The priv device
 | 
						|
 *
 | 
						|
 * Returns zero if succeed.
 | 
						|
 */
 | 
						|
int it6621_force_pdiv(struct it6621_priv *priv)
 | 
						|
{
 | 
						|
	unsigned int tbclk_sel;
 | 
						|
	unsigned int xp_pdiv;
 | 
						|
	unsigned int xp_gain;
 | 
						|
	unsigned int val;
 | 
						|
	unsigned int expect;
 | 
						|
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_CLK_CTRL1, &val);
 | 
						|
	tbclk_sel = val & IT6621_TBCLK_SEL;
 | 
						|
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_TX_AFE1, &val);
 | 
						|
	xp_pdiv = val & IT6621_TX_XP_PDIV;
 | 
						|
	xp_gain = (val & IT6621_TX_XP_GAIN) >> 2;
 | 
						|
 | 
						|
	dev_dbg(priv->dev, "ACLK: %d\n", priv->aclk);
 | 
						|
 | 
						|
	expect = 0xff;
 | 
						|
	switch (tbclk_sel) {
 | 
						|
	case IT6621_TBCLK_AICLK_MUL_32:
 | 
						|
		if (xp_pdiv != 0)
 | 
						|
			expect = 0;
 | 
						|
		break;
 | 
						|
	case IT6621_TBCLK_AICLK_MUL_16:
 | 
						|
		if ((priv->aclk < 3584) && (xp_pdiv != 0))
 | 
						|
			expect = 0;
 | 
						|
		else if ((priv->aclk > 3584) && (priv->aclk < 7168) &&
 | 
						|
			 (xp_pdiv != 1))
 | 
						|
			expect = 1;
 | 
						|
		break;
 | 
						|
	case IT6621_TBCLK_AICLK_MUL_8:
 | 
						|
		if ((priv->aclk < 3584) && (xp_pdiv != 0))
 | 
						|
			expect = 0;
 | 
						|
		else if ((priv->aclk > 3584) && (priv->aclk < 7168) &&
 | 
						|
			 (xp_pdiv != 1))
 | 
						|
			expect = 1;
 | 
						|
		else if ((priv->aclk > 7168) && (priv->aclk < 14336) &&
 | 
						|
			 (xp_pdiv != 3))
 | 
						|
			expect = 3;
 | 
						|
		break;
 | 
						|
	case IT6621_TBCLK_AICLK_MUL_4:
 | 
						|
		if ((priv->aclk < 7168) && xp_pdiv != 0)
 | 
						|
			expect = 0;
 | 
						|
		else if ((priv->aclk > 7168) && (priv->aclk < 14336) &&
 | 
						|
			 (xp_pdiv != 1))
 | 
						|
			expect = 1;
 | 
						|
		else if ((priv->aclk > 14336) && (priv->aclk < 28672) &&
 | 
						|
			 (xp_pdiv != 3))
 | 
						|
			expect = 3;
 | 
						|
		break;
 | 
						|
	case IT6621_TBCLK_AICLK_MUL_2:
 | 
						|
		if ((priv->aclk < 14336) && xp_pdiv != 0)
 | 
						|
			expect = 0;
 | 
						|
		else if ((priv->aclk > 14336) && (priv->aclk < 28672) &&
 | 
						|
			 (xp_pdiv != 1))
 | 
						|
			expect = 1;
 | 
						|
		else if ((priv->aclk > 28672) && (priv->aclk < 57334) &&
 | 
						|
			 (xp_pdiv != 3))
 | 
						|
			expect = 3;
 | 
						|
		break;
 | 
						|
	case IT6621_TBCLK_AICLK:
 | 
						|
		if ((priv->aclk < 28672) && (xp_pdiv != 0))
 | 
						|
			expect = 0;
 | 
						|
		else if ((priv->aclk > 28672) && (priv->aclk < 57334) &&
 | 
						|
			 (xp_pdiv != 1))
 | 
						|
			expect = 1;
 | 
						|
		else if ((priv->aclk > 57334) && (priv->aclk < 114668) &&
 | 
						|
			 (xp_pdiv != 3))
 | 
						|
			expect = 3;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (expect != 0xff)
 | 
						|
		dev_warn(priv->dev, "Wrong XP PDIV, detect %d, expected %d\n",
 | 
						|
			 xp_pdiv, expect);
 | 
						|
 | 
						|
	expect = 0xff;
 | 
						|
	switch (tbclk_sel) {
 | 
						|
	case IT6621_TBCLK_AICLK:
 | 
						|
		if ((priv->aclk > 3584) && (priv->aclk < 7168) &&
 | 
						|
		    (xp_gain != 0))
 | 
						|
			expect = 0;
 | 
						|
		break;
 | 
						|
	case IT6621_TBCLK_AICLK_MUL_2:
 | 
						|
		if ((priv->aclk < 3584) && (xp_gain != 0))
 | 
						|
			expect = 0;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		if (xp_gain != 1)
 | 
						|
			expect = 1;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (expect != 0xff)
 | 
						|
		dev_warn(priv->dev, "Wrong XP GAIN, detect %d, expected %d\n",
 | 
						|
			 xp_gain, expect);
 | 
						|
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_TX_AFE1, &val);
 | 
						|
	xp_pdiv = val & IT6621_TX_XP_PDIV;
 | 
						|
	xp_gain = (val & IT6621_TX_XP_GAIN) >> 2;
 | 
						|
	dev_dbg(priv->dev, "TBCLK SEL: 0x%02X, XP PDIV: %d, XP GAIN: %d\n",
 | 
						|
		tbclk_sel, xp_pdiv, xp_gain);
 | 
						|
 | 
						|
	usleep_range(10000, 11000);
 | 
						|
	regmap_update_bits(priv->regmap, IT6621_REG_DMAC_CTRL1,
 | 
						|
			   IT6621_TX_MANUAL_RESET_EN_SEL,
 | 
						|
			   IT6621_TX_MANUAL_RESET_EN);
 | 
						|
	regmap_update_bits(priv->regmap, IT6621_REG_DMAC_CTRL1,
 | 
						|
			   IT6621_TX_MANUAL_RESET_EN_SEL,
 | 
						|
			   IT6621_TX_MANUAL_RESET_DIS);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void it6621_get_aclk(struct it6621_priv *priv)
 | 
						|
{
 | 
						|
	unsigned int aclk_avg;
 | 
						|
	unsigned int aclk_pred2;
 | 
						|
	unsigned int aclk_pred4;
 | 
						|
	unsigned int aclk_valid;
 | 
						|
	unsigned int aclk_stb;
 | 
						|
	unsigned int refclk;
 | 
						|
	unsigned int tmp;
 | 
						|
 | 
						|
	it6621_get_refclk(priv, &refclk);
 | 
						|
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_CLK_DET8, &aclk_avg);
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_CLK_DET9, &tmp);
 | 
						|
	aclk_avg += ((tmp & IT6621_DET_ACLK_AVG_HIGH) << 8);
 | 
						|
	aclk_pred2 = (tmp & IT6621_DET_ACLK_PRE_DIV_2) >> 4;
 | 
						|
	aclk_pred4 = (tmp & IT6621_DET_ACLK_PRE_DIV_4) >> 5;
 | 
						|
	aclk_valid = (tmp & IT6621_DET_ACLK_FREQ_VALID) >> 6;
 | 
						|
	aclk_stb = (tmp & IT6621_DET_ACLK_FREQ_STB) >> 7;
 | 
						|
 | 
						|
	priv->aclk = refclk * 128 / aclk_avg;
 | 
						|
 | 
						|
	if (aclk_pred4)
 | 
						|
		priv->aclk *= 4;
 | 
						|
	else if (aclk_pred2)
 | 
						|
		priv->aclk *= 2;
 | 
						|
 | 
						|
	dev_dbg(priv->dev, "ACLK: %uMHz, valid: %d, stable: %d\n",
 | 
						|
		priv->aclk / 1000, aclk_valid, aclk_stb);
 | 
						|
}
 | 
						|
 | 
						|
void it6621_get_bclk(struct it6621_priv *priv)
 | 
						|
{
 | 
						|
	unsigned int bclk_avg;
 | 
						|
	unsigned int bclk_pred2;
 | 
						|
	unsigned int bclk_pred4;
 | 
						|
	unsigned int bclk_valid;
 | 
						|
	unsigned int bclk_stb;
 | 
						|
	unsigned int refclk;
 | 
						|
	unsigned int tmp;
 | 
						|
 | 
						|
	it6621_get_refclk(priv, &refclk);
 | 
						|
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_CLK_DET10, &bclk_avg);
 | 
						|
	regmap_read(priv->regmap, IT6621_REG_CLK_DET11, &tmp);
 | 
						|
	bclk_avg += ((tmp & IT6621_DET_BCLK_AVG_HIGH) << 8);
 | 
						|
	bclk_pred2 = (tmp & IT6621_DET_BCLK_PRE_DIV_2) >> 4;
 | 
						|
	bclk_pred4 = (tmp & IT6621_DET_BCLK_PRE_DIV_4) >> 5;
 | 
						|
	bclk_valid = (tmp & IT6621_DET_BCLK_FREQ_VALID) >> 6;
 | 
						|
	bclk_stb = (tmp & IT6621_DET_BCLK_FREQ_STB) >> 7;
 | 
						|
 | 
						|
	priv->bclk = refclk * 128 / bclk_avg;
 | 
						|
 | 
						|
	if (bclk_pred4)
 | 
						|
		priv->bclk *= 4;
 | 
						|
	else if (bclk_pred2)
 | 
						|
		priv->bclk *= 2;
 | 
						|
 | 
						|
	dev_dbg(priv->dev, "BCLK: %uMHz, valid: %d, stable: %d\n",
 | 
						|
		priv->bclk / 1000, bclk_valid, bclk_stb);
 | 
						|
}
 |