提交 f0684c1a 编写于 作者: H Heiko Stuebner 提交者: Kishon Vijay Abraham I

phy/rockchip: inno-dsidphy: generalize parameter handling

During review it came to light that exposing the pll clock outside is
not the right approach and struct phy_configure_opts_mipi_dphy exists
just for that reason to transfer parameters to the phy.

So drop the exposed clock and rely on the phy configure options
to bring in the correct rate. That way we can also just drop the
open coded timing struct and default values function.

Fixes: b7535a3b ("phy/rockchip: Add support for Innosilicon MIPI/LVDS/TTL PHY")
Signed-off-by: NHeiko Stuebner <heiko.stuebner@theobroma-systems.com>
Signed-off-by: NKishon Vijay Abraham I <kishon@ti.com>
上级 cb18b9a9
...@@ -39,6 +39,7 @@ config PHY_ROCKCHIP_INNO_DSIDPHY ...@@ -39,6 +39,7 @@ config PHY_ROCKCHIP_INNO_DSIDPHY
tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver" tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
select GENERIC_PHY select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help help
Enable this to support the Rockchip MIPI/LVDS/TTL PHY with Enable this to support the Rockchip MIPI/LVDS/TTL PHY with
Innosilicon IP block. Innosilicon IP block.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
...@@ -167,31 +168,6 @@ ...@@ -167,31 +168,6 @@
#define DSI_PHY_STATUS 0xb0 #define DSI_PHY_STATUS 0xb0
#define PHY_LOCK BIT(0) #define PHY_LOCK BIT(0)
struct mipi_dphy_timing {
unsigned int clkmiss;
unsigned int clkpost;
unsigned int clkpre;
unsigned int clkprepare;
unsigned int clksettle;
unsigned int clktermen;
unsigned int clktrail;
unsigned int clkzero;
unsigned int dtermen;
unsigned int eot;
unsigned int hsexit;
unsigned int hsprepare;
unsigned int hszero;
unsigned int hssettle;
unsigned int hsskip;
unsigned int hstrail;
unsigned int init;
unsigned int lpx;
unsigned int taget;
unsigned int tago;
unsigned int tasure;
unsigned int wakeup;
};
struct inno_dsidphy { struct inno_dsidphy {
struct device *dev; struct device *dev;
struct clk *ref_clk; struct clk *ref_clk;
...@@ -201,7 +177,9 @@ struct inno_dsidphy { ...@@ -201,7 +177,9 @@ struct inno_dsidphy {
void __iomem *host_base; void __iomem *host_base;
struct reset_control *rst; struct reset_control *rst;
enum phy_mode mode; enum phy_mode mode;
struct phy_configure_opts_mipi_dphy dphy_cfg;
struct clk *pll_clk;
struct { struct {
struct clk_hw hw; struct clk_hw hw;
u8 prediv; u8 prediv;
...@@ -238,37 +216,79 @@ static void phy_update_bits(struct inno_dsidphy *inno, ...@@ -238,37 +216,79 @@ static void phy_update_bits(struct inno_dsidphy *inno,
writel(tmp, inno->phy_base + reg); writel(tmp, inno->phy_base + reg);
} }
static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
unsigned long period) unsigned long rate)
{ {
/* Global Operation Timing Parameters */ unsigned long prate = clk_get_rate(inno->ref_clk);
timing->clkmiss = 0; unsigned long best_freq = 0;
timing->clkpost = 70000 + 52 * period; unsigned long fref, fout;
timing->clkpre = 8 * period; u8 min_prediv, max_prediv;
timing->clkprepare = 65000; u8 _prediv, best_prediv = 1;
timing->clksettle = 95000; u16 _fbdiv, best_fbdiv = 1;
timing->clktermen = 0; u32 min_delta = UINT_MAX;
timing->clktrail = 80000;
timing->clkzero = 260000; /*
timing->dtermen = 0; * The PLL output frequency can be calculated using a simple formula:
timing->eot = 0; * PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
timing->hsexit = 120000; * PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
timing->hsprepare = 65000 + 4 * period; */
timing->hszero = 145000 + 6 * period; fref = prate / 2;
timing->hssettle = 85000 + 6 * period; if (rate > 1000000000UL)
timing->hsskip = 40000; fout = 1000000000UL;
timing->hstrail = max(8 * period, 60000 + 4 * period); else
timing->init = 100000000; fout = rate;
timing->lpx = 60000;
timing->taget = 5 * timing->lpx; /* 5Mhz < Fref / prediv < 40MHz */
timing->tago = 4 * timing->lpx; min_prediv = DIV_ROUND_UP(fref, 40000000);
timing->tasure = 2 * timing->lpx; max_prediv = fref / 5000000;
timing->wakeup = 1000000000;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp;
u32 delta;
tmp = (u64)fout * _prediv;
do_div(tmp, fref);
_fbdiv = tmp;
/*
* The possible settings of feedback divider are
* 12, 13, 14, 16, ~ 511
*/
if (_fbdiv == 15)
continue;
if (_fbdiv < 12 || _fbdiv > 511)
continue;
tmp = (u64)_fbdiv * fref;
do_div(tmp, _prediv);
delta = abs(fout - tmp);
if (!delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
break;
} else if (delta < min_delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
min_delta = delta;
}
}
if (best_freq) {
inno->pll.prediv = best_prediv;
inno->pll.fbdiv = best_fbdiv;
inno->pll.rate = best_freq;
}
return best_freq;
} }
static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
{ {
struct mipi_dphy_timing gotp; struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
const struct { const struct {
unsigned long rate; unsigned long rate;
u8 hs_prepare; u8 hs_prepare;
...@@ -288,12 +308,14 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) ...@@ -288,12 +308,14 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
{ 800000000, 0x21, 0x1f, 0x09, 0x29}, { 800000000, 0x21, 0x1f, 0x09, 0x29},
{1000000000, 0x09, 0x20, 0x09, 0x27}, {1000000000, 0x09, 0x20, 0x09, 0x27},
}; };
u32 t_txbyteclkhs, t_txclkesc, ui; u32 t_txbyteclkhs, t_txclkesc;
u32 txbyteclkhs, txclkesc, esc_clk_div; u32 txbyteclkhs, txclkesc, esc_clk_div;
u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait; u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero; u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
unsigned int i; unsigned int i;
inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
/* Select MIPI mode */ /* Select MIPI mode */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x03, phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
MODE_ENABLE_MASK, MIPI_MODE_ENABLE); MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
...@@ -328,32 +350,27 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) ...@@ -328,32 +350,27 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
txclkesc = txbyteclkhs / esc_clk_div; txclkesc = txbyteclkhs / esc_clk_div;
t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc); t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc);
ui = div_u64(PSEC_PER_SEC, inno->pll.rate);
memset(&gotp, 0, sizeof(gotp));
mipi_dphy_timing_get_default(&gotp, ui);
/* /*
* The value of counter for HS Ths-exit * The value of counter for HS Ths-exit
* Ths-exit = Tpin_txbyteclkhs * value * Ths-exit = Tpin_txbyteclkhs * value
*/ */
hs_exit = DIV_ROUND_UP(gotp.hsexit, t_txbyteclkhs); hs_exit = DIV_ROUND_UP(cfg->hs_exit, t_txbyteclkhs);
/* /*
* The value of counter for HS Tclk-post * The value of counter for HS Tclk-post
* Tclk-post = Tpin_txbyteclkhs * value * Tclk-post = Tpin_txbyteclkhs * value
*/ */
clk_post = DIV_ROUND_UP(gotp.clkpost, t_txbyteclkhs); clk_post = DIV_ROUND_UP(cfg->clk_post, t_txbyteclkhs);
/* /*
* The value of counter for HS Tclk-pre * The value of counter for HS Tclk-pre
* Tclk-pre = Tpin_txbyteclkhs * value * Tclk-pre = Tpin_txbyteclkhs * value
*/ */
clk_pre = DIV_ROUND_UP(gotp.clkpre, t_txbyteclkhs); clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs);
/* /*
* The value of counter for HS Tlpx Time * The value of counter for HS Tlpx Time
* Tlpx = Tpin_txbyteclkhs * (2 + value) * Tlpx = Tpin_txbyteclkhs * (2 + value)
*/ */
lpx = DIV_ROUND_UP(gotp.lpx, t_txbyteclkhs); lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
if (lpx >= 2) if (lpx >= 2)
lpx -= 2; lpx -= 2;
...@@ -362,19 +379,19 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) ...@@ -362,19 +379,19 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
* Tta-go for turnaround * Tta-go for turnaround
* Tta-go = Ttxclkesc * value * Tta-go = Ttxclkesc * value
*/ */
ta_go = DIV_ROUND_UP(gotp.tago, t_txclkesc); ta_go = DIV_ROUND_UP(cfg->ta_go, t_txclkesc);
/* /*
* The value of counter for HS Tta-sure * The value of counter for HS Tta-sure
* Tta-sure for turnaround * Tta-sure for turnaround
* Tta-sure = Ttxclkesc * value * Tta-sure = Ttxclkesc * value
*/ */
ta_sure = DIV_ROUND_UP(gotp.tasure, t_txclkesc); ta_sure = DIV_ROUND_UP(cfg->ta_sure, t_txclkesc);
/* /*
* The value of counter for HS Tta-wait * The value of counter for HS Tta-wait
* Tta-wait for turnaround * Tta-wait for turnaround
* Tta-wait = Ttxclkesc * value * Tta-wait = Ttxclkesc * value
*/ */
ta_wait = DIV_ROUND_UP(gotp.taget, t_txclkesc); ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
for (i = 0; i < ARRAY_SIZE(timings); i++) for (i = 0; i < ARRAY_SIZE(timings); i++)
if (inno->pll.rate <= timings[i].rate) if (inno->pll.rate <= timings[i].rate)
...@@ -479,6 +496,7 @@ static int inno_dsidphy_power_on(struct phy *phy) ...@@ -479,6 +496,7 @@ static int inno_dsidphy_power_on(struct phy *phy)
struct inno_dsidphy *inno = phy_get_drvdata(phy); struct inno_dsidphy *inno = phy_get_drvdata(phy);
clk_prepare_enable(inno->pclk_phy); clk_prepare_enable(inno->pclk_phy);
clk_prepare_enable(inno->ref_clk);
pm_runtime_get_sync(inno->dev); pm_runtime_get_sync(inno->dev);
/* Bandgap power on */ /* Bandgap power on */
...@@ -524,6 +542,7 @@ static int inno_dsidphy_power_off(struct phy *phy) ...@@ -524,6 +542,7 @@ static int inno_dsidphy_power_off(struct phy *phy)
LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN); LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN);
pm_runtime_put(inno->dev); pm_runtime_put(inno->dev);
clk_disable_unprepare(inno->ref_clk);
clk_disable_unprepare(inno->pclk_phy); clk_disable_unprepare(inno->pclk_phy);
return 0; return 0;
...@@ -546,168 +565,32 @@ static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode, ...@@ -546,168 +565,32 @@ static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode,
return 0; return 0;
} }
static const struct phy_ops inno_dsidphy_ops = { static int inno_dsidphy_configure(struct phy *phy,
.set_mode = inno_dsidphy_set_mode, union phy_configure_opts *opts)
.power_on = inno_dsidphy_power_on,
.power_off = inno_dsidphy_power_off,
.owner = THIS_MODULE,
};
static unsigned long inno_dsidphy_pll_round_rate(struct inno_dsidphy *inno,
unsigned long prate,
unsigned long rate,
u8 *prediv, u16 *fbdiv)
{
unsigned long best_freq = 0;
unsigned long fref, fout;
u8 min_prediv, max_prediv;
u8 _prediv, best_prediv = 1;
u16 _fbdiv, best_fbdiv = 1;
u32 min_delta = UINT_MAX;
/*
* The PLL output frequency can be calculated using a simple formula:
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
*/
fref = prate / 2;
if (rate > 1000000000UL)
fout = 1000000000UL;
else
fout = rate;
/* 5Mhz < Fref / prediv < 40MHz */
min_prediv = DIV_ROUND_UP(fref, 40000000);
max_prediv = fref / 5000000;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp;
u32 delta;
tmp = (u64)fout * _prediv;
do_div(tmp, fref);
_fbdiv = tmp;
/*
* The possible settings of feedback divider are
* 12, 13, 14, 16, ~ 511
*/
if (_fbdiv == 15)
continue;
if (_fbdiv < 12 || _fbdiv > 511)
continue;
tmp = (u64)_fbdiv * fref;
do_div(tmp, _prediv);
delta = abs(fout - tmp);
if (!delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
break;
} else if (delta < min_delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
min_delta = delta;
}
}
if (best_freq) {
*prediv = best_prediv;
*fbdiv = best_fbdiv;
}
return best_freq;
}
static long inno_dsidphy_pll_clk_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *prate)
{ {
struct inno_dsidphy *inno = hw_to_inno(hw); struct inno_dsidphy *inno = phy_get_drvdata(phy);
unsigned long fout; int ret;
u16 fbdiv = 1;
u8 prediv = 1;
fout = inno_dsidphy_pll_round_rate(inno, *prate, rate,
&prediv, &fbdiv);
return fout;
}
static int inno_dsidphy_pll_clk_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct inno_dsidphy *inno = hw_to_inno(hw);
unsigned long fout;
u16 fbdiv = 1;
u8 prediv = 1;
fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate, if (inno->mode != PHY_MODE_MIPI_DPHY)
&prediv, &fbdiv); return -EINVAL;
dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n", ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
parent_rate, fout, prediv, fbdiv); if (ret)
return ret;
inno->pll.prediv = prediv; memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg));
inno->pll.fbdiv = fbdiv;
inno->pll.rate = fout;
return 0; return 0;
} }
static unsigned long static const struct phy_ops inno_dsidphy_ops = {
inno_dsidphy_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) .configure = inno_dsidphy_configure,
{ .set_mode = inno_dsidphy_set_mode,
struct inno_dsidphy *inno = hw_to_inno(hw); .power_on = inno_dsidphy_power_on,
.power_off = inno_dsidphy_power_off,
/* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 */ .owner = THIS_MODULE,
return (prate / inno->pll.prediv * inno->pll.fbdiv) / 2;
}
static const struct clk_ops inno_dsidphy_pll_clk_ops = {
.round_rate = inno_dsidphy_pll_clk_round_rate,
.set_rate = inno_dsidphy_pll_clk_set_rate,
.recalc_rate = inno_dsidphy_pll_clk_recalc_rate,
}; };
static int inno_dsidphy_pll_register(struct inno_dsidphy *inno)
{
struct device *dev = inno->dev;
struct clk *clk;
const char *parent_name;
struct clk_init_data init;
int ret;
parent_name = __clk_get_name(inno->ref_clk);
init.name = "mipi_dphy_pll";
ret = of_property_read_string(dev->of_node, "clock-output-names",
&init.name);
if (ret < 0)
dev_dbg(dev, "phy should set clock-output-names property\n");
init.ops = &inno_dsidphy_pll_clk_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
inno->pll.hw.init = &init;
clk = devm_clk_register(dev, &inno->pll.hw);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(dev, "failed to register PLL: %d\n", ret);
return ret;
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
&inno->pll.hw);
}
static int inno_dsidphy_probe(struct platform_device *pdev) static int inno_dsidphy_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -764,10 +647,6 @@ static int inno_dsidphy_probe(struct platform_device *pdev) ...@@ -764,10 +647,6 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
return ret; return ret;
} }
ret = inno_dsidphy_pll_register(inno);
if (ret)
return ret;
pm_runtime_enable(dev); pm_runtime_enable(dev);
return 0; return 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册