summaryrefslogtreecommitdiff
path: root/drivers/phy/freescale
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy/freescale')
-rw-r--r--drivers/phy/freescale/Kconfig1
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c44
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c324
-rw-r--r--drivers/phy/freescale/phy-fsl-samsung-hdmi.c140
4 files changed, 405 insertions, 104 deletions
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index dcd9acff6d01..81f53564ee15 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -5,6 +5,7 @@ if (ARCH_MXC && ARM64) || COMPILE_TEST
config PHY_FSL_IMX8MQ_USB
tristate "Freescale i.MX8M USB3 PHY"
depends on OF && HAS_IOMEM
+ depends on TYPEC || TYPEC=n
select GENERIC_PHY
default ARCH_MXC && ARM64
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
index e98361dcdead..68fcc8114d75 100644
--- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -141,15 +141,9 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
IMX8MM_GPR_PCIE_REF_CLK_PLL);
usleep_range(100, 200);
- switch (imx8_phy->drvdata->variant) {
- case IMX8MP:
- reset_control_deassert(imx8_phy->perst);
- fallthrough;
- case IMX8MM:
- reset_control_deassert(imx8_phy->reset);
- usleep_range(200, 500);
- break;
- }
+ reset_control_deassert(imx8_phy->perst);
+ reset_control_deassert(imx8_phy->reset);
+ usleep_range(200, 500);
/* Do the PHY common block reset */
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
@@ -162,6 +156,16 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
return ret;
}
+static int imx8_pcie_phy_power_off(struct phy *phy)
+{
+ struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
+
+ reset_control_assert(imx8_phy->reset);
+ reset_control_assert(imx8_phy->perst);
+
+ return 0;
+}
+
static int imx8_pcie_phy_init(struct phy *phy)
{
struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
@@ -182,6 +186,7 @@ static const struct phy_ops imx8_pcie_phy_ops = {
.init = imx8_pcie_phy_init,
.exit = imx8_pcie_phy_exit,
.power_on = imx8_pcie_phy_power_on,
+ .power_off = imx8_pcie_phy_power_off,
.owner = THIS_MODULE,
};
@@ -233,24 +238,21 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
imx8_phy->clkreq_unused = false;
imx8_phy->clk = devm_clk_get(dev, "ref");
- if (IS_ERR(imx8_phy->clk)) {
- dev_err(dev, "failed to get imx pcie phy clock\n");
- return PTR_ERR(imx8_phy->clk);
- }
+ if (IS_ERR(imx8_phy->clk))
+ return dev_err_probe(dev, PTR_ERR(imx8_phy->clk),
+ "failed to get imx pcie phy clock\n");
/* Grab GPR config register range */
imx8_phy->iomuxc_gpr =
syscon_regmap_lookup_by_compatible(imx8_phy->drvdata->gpr);
- if (IS_ERR(imx8_phy->iomuxc_gpr)) {
- dev_err(dev, "unable to find iomuxc registers\n");
- return PTR_ERR(imx8_phy->iomuxc_gpr);
- }
+ if (IS_ERR(imx8_phy->iomuxc_gpr))
+ return dev_err_probe(dev, PTR_ERR(imx8_phy->iomuxc_gpr),
+ "unable to find iomuxc registers\n");
imx8_phy->reset = devm_reset_control_get_exclusive(dev, "pciephy");
- if (IS_ERR(imx8_phy->reset)) {
- dev_err(dev, "Failed to get PCIEPHY reset control\n");
- return PTR_ERR(imx8_phy->reset);
- }
+ if (IS_ERR(imx8_phy->reset))
+ return dev_err_probe(dev, PTR_ERR(imx8_phy->reset),
+ "Failed to get PCIEPHY reset control\n");
if (imx8_phy->drvdata->variant == IMX8MP) {
imx8_phy->perst =
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index adc6394626ce..b94f242420fc 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -10,6 +10,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/usb/typec_mux.h>
#define PHY_CTRL0 0x0
#define PHY_CTRL0_REF_SSP_EN BIT(2)
@@ -50,11 +51,66 @@
#define PHY_TUNE_DEFAULT 0xffffffff
+#define TCA_CLK_RST 0x00
+#define TCA_CLK_RST_SW BIT(9)
+#define TCA_CLK_RST_REF_CLK_EN BIT(1)
+#define TCA_CLK_RST_SUSPEND_CLK_EN BIT(0)
+
+#define TCA_INTR_EN 0x04
+#define TCA_INTR_STS 0x08
+
+#define TCA_GCFG 0x10
+#define TCA_GCFG_ROLE_HSTDEV BIT(4)
+#define TCA_GCFG_OP_MODE GENMASK(1, 0)
+#define TCA_GCFG_OP_MODE_SYSMODE 0
+#define TCA_GCFG_OP_MODE_SYNCMODE 1
+
+#define TCA_TCPC 0x14
+#define TCA_TCPC_VALID BIT(4)
+#define TCA_TCPC_LOW_POWER_EN BIT(3)
+#define TCA_TCPC_ORIENTATION_NORMAL BIT(2)
+#define TCA_TCPC_MUX_CONTRL GENMASK(1, 0)
+#define TCA_TCPC_MUX_CONTRL_NO_CONN 0
+#define TCA_TCPC_MUX_CONTRL_USB_CONN 1
+
+#define TCA_SYSMODE_CFG 0x18
+#define TCA_SYSMODE_TCPC_DISABLE BIT(3)
+#define TCA_SYSMODE_TCPC_FLIP BIT(2)
+
+#define TCA_CTRLSYNCMODE_CFG0 0x20
+#define TCA_CTRLSYNCMODE_CFG1 0x20
+
+#define TCA_PSTATE 0x30
+#define TCA_PSTATE_CM_STS BIT(4)
+#define TCA_PSTATE_TX_STS BIT(3)
+#define TCA_PSTATE_RX_PLL_STS BIT(2)
+#define TCA_PSTATE_PIPE0_POWER_DOWN GENMASK(1, 0)
+
+#define TCA_GEN_STATUS 0x34
+#define TCA_GEN_DEV_POR BIT(12)
+#define TCA_GEN_REF_CLK_SEL BIT(8)
+#define TCA_GEN_TYPEC_FLIP_INVERT BIT(4)
+#define TCA_GEN_PHY_TYPEC_DISABLE BIT(3)
+#define TCA_GEN_PHY_TYPEC_FLIP BIT(2)
+
+#define TCA_VBUS_CTRL 0x40
+#define TCA_VBUS_STATUS 0x44
+
+#define TCA_INFO 0xfc
+
+struct tca_blk {
+ struct typec_switch_dev *sw;
+ void __iomem *base;
+ struct mutex mutex;
+ enum typec_orientation orientation;
+};
+
struct imx8mq_usb_phy {
struct phy *phy;
struct clk *clk;
void __iomem *base;
struct regulator *vbus;
+ struct tca_blk *tca;
u32 pcs_tx_swing_full;
u32 pcs_tx_deemph_3p5db;
u32 tx_vref_tune;
@@ -64,6 +120,172 @@ struct imx8mq_usb_phy {
u32 comp_dis_tune;
};
+
+static void tca_blk_orientation_set(struct tca_blk *tca,
+ enum typec_orientation orientation);
+
+#ifdef CONFIG_TYPEC
+
+static int tca_blk_typec_switch_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct imx8mq_usb_phy *imx_phy = typec_switch_get_drvdata(sw);
+ struct tca_blk *tca = imx_phy->tca;
+ int ret;
+
+ if (tca->orientation == orientation)
+ return 0;
+
+ ret = clk_prepare_enable(imx_phy->clk);
+ if (ret)
+ return ret;
+
+ tca_blk_orientation_set(tca, orientation);
+ clk_disable_unprepare(imx_phy->clk);
+
+ return 0;
+}
+
+static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device *pdev,
+ struct imx8mq_usb_phy *imx_phy)
+{
+ struct device *dev = &pdev->dev;
+ struct typec_switch_dev *sw;
+ struct typec_switch_desc sw_desc = { };
+
+ sw_desc.drvdata = imx_phy;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = tca_blk_typec_switch_set;
+ sw_desc.name = NULL;
+
+ sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(sw)) {
+ dev_err(dev, "Error register tca orientation switch: %ld",
+ PTR_ERR(sw));
+ return NULL;
+ }
+
+ return sw;
+}
+
+static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
+{
+ typec_switch_unregister(sw);
+}
+
+#else
+
+static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device *pdev,
+ struct imx8mq_usb_phy *imx_phy)
+{
+ return NULL;
+}
+
+static void tca_blk_put_typec_switch(struct typec_switch_dev *sw) {}
+
+#endif /* CONFIG_TYPEC */
+
+static void tca_blk_orientation_set(struct tca_blk *tca,
+ enum typec_orientation orientation)
+{
+ u32 val;
+
+ mutex_lock(&tca->mutex);
+
+ if (orientation == TYPEC_ORIENTATION_NONE) {
+ /*
+ * use Controller Synced Mode for TCA low power enable and
+ * put PHY to USB safe state.
+ */
+ val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYNCMODE);
+ writel(val, tca->base + TCA_GCFG);
+
+ val = TCA_TCPC_VALID | TCA_TCPC_LOW_POWER_EN;
+ writel(val, tca->base + TCA_TCPC);
+
+ goto out;
+ }
+
+ /* use System Configuration Mode for TCA mux control. */
+ val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYSMODE);
+ writel(val, tca->base + TCA_GCFG);
+
+ /* Disable TCA module */
+ val = readl(tca->base + TCA_SYSMODE_CFG);
+ val |= TCA_SYSMODE_TCPC_DISABLE;
+ writel(val, tca->base + TCA_SYSMODE_CFG);
+
+ if (orientation == TYPEC_ORIENTATION_REVERSE)
+ val |= TCA_SYSMODE_TCPC_FLIP;
+ else if (orientation == TYPEC_ORIENTATION_NORMAL)
+ val &= ~TCA_SYSMODE_TCPC_FLIP;
+
+ writel(val, tca->base + TCA_SYSMODE_CFG);
+
+ /* Enable TCA module */
+ val &= ~TCA_SYSMODE_TCPC_DISABLE;
+ writel(val, tca->base + TCA_SYSMODE_CFG);
+
+out:
+ tca->orientation = orientation;
+ mutex_unlock(&tca->mutex);
+}
+
+static void tca_blk_init(struct tca_blk *tca)
+{
+ u32 val;
+
+ /* reset XBar block */
+ val = readl(tca->base + TCA_CLK_RST);
+ val &= ~TCA_CLK_RST_SW;
+ writel(val, tca->base + TCA_CLK_RST);
+
+ udelay(100);
+
+ /* clear reset */
+ val |= TCA_CLK_RST_SW;
+ writel(val, tca->base + TCA_CLK_RST);
+
+ tca_blk_orientation_set(tca, tca->orientation);
+}
+
+static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
+ struct imx8mq_usb_phy *imx_phy)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct tca_blk *tca;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return NULL;
+
+ tca = devm_kzalloc(dev, sizeof(*tca), GFP_KERNEL);
+ if (!tca)
+ return ERR_PTR(-ENOMEM);
+
+ tca->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tca->base))
+ return ERR_CAST(tca->base);
+
+ mutex_init(&tca->mutex);
+
+ tca->orientation = TYPEC_ORIENTATION_NORMAL;
+ tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
+
+ return tca;
+}
+
+static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
+{
+ struct tca_blk *tca = imx_phy->tca;
+
+ if (!tca)
+ return;
+
+ tca_blk_put_typec_switch(tca->sw);
+}
+
static u32 phy_tx_vref_tune_from_property(u32 percent)
{
percent = clamp(percent, 94U, 124U);
@@ -71,6 +293,28 @@ static u32 phy_tx_vref_tune_from_property(u32 percent)
return DIV_ROUND_CLOSEST(percent - 94U, 2);
}
+static u32 imx95_phy_tx_vref_tune_from_property(u32 percent)
+{
+ percent = clamp(percent, 90U, 108U);
+
+ switch (percent) {
+ case 90 ... 91:
+ percent = 0;
+ break;
+ case 92 ... 96:
+ percent -= 91;
+ break;
+ case 97 ... 104:
+ percent -= 92;
+ break;
+ case 105 ... 108:
+ percent -= 93;
+ break;
+ }
+
+ return percent;
+}
+
static u32 phy_tx_rise_tune_from_property(u32 percent)
{
switch (percent) {
@@ -85,6 +329,22 @@ static u32 phy_tx_rise_tune_from_property(u32 percent)
}
}
+static u32 imx95_phy_tx_rise_tune_from_property(u32 percent)
+{
+ percent = clamp(percent, 90U, 120U);
+
+ switch (percent) {
+ case 90 ... 99:
+ return 3;
+ case 101 ... 115:
+ return 1;
+ case 116 ... 120:
+ return 0;
+ default:
+ return 2;
+ }
+}
+
static u32 phy_tx_preemp_amp_tune_from_property(u32 microamp)
{
microamp = min(microamp, 1800U);
@@ -95,12 +355,12 @@ static u32 phy_tx_preemp_amp_tune_from_property(u32 microamp)
static u32 phy_tx_vboost_level_from_property(u32 microvolt)
{
switch (microvolt) {
- case 0 ... 960:
- return 0;
- case 961 ... 1160:
- return 2;
- default:
+ case 1156:
+ return 5;
+ case 844:
return 3;
+ default:
+ return 4;
}
}
@@ -130,6 +390,29 @@ static u32 phy_comp_dis_tune_from_property(u32 percent)
return 7;
}
}
+
+static u32 imx95_phy_comp_dis_tune_from_property(u32 percent)
+{
+ percent = clamp(percent, 94, 104);
+
+ switch (percent) {
+ case 94 ... 95:
+ percent = 0;
+ break;
+ case 96 ... 98:
+ percent -= 95;
+ break;
+ case 99 ... 102:
+ percent -= 96;
+ break;
+ case 103 ... 104:
+ percent -= 97;
+ break;
+ }
+
+ return percent;
+}
+
static u32 phy_pcs_tx_swing_full_from_property(u32 percent)
{
percent = min(percent, 100U);
@@ -140,10 +423,17 @@ static u32 phy_pcs_tx_swing_full_from_property(u32 percent)
static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy)
{
struct device *dev = imx_phy->phy->dev.parent;
+ bool is_imx95 = false;
+
+ if (device_is_compatible(dev, "fsl,imx95-usb-phy"))
+ is_imx95 = true;
if (device_property_read_u32(dev, "fsl,phy-tx-vref-tune-percent",
&imx_phy->tx_vref_tune))
imx_phy->tx_vref_tune = PHY_TUNE_DEFAULT;
+ else if (is_imx95)
+ imx_phy->tx_vref_tune =
+ imx95_phy_tx_vref_tune_from_property(imx_phy->tx_vref_tune);
else
imx_phy->tx_vref_tune =
phy_tx_vref_tune_from_property(imx_phy->tx_vref_tune);
@@ -151,6 +441,9 @@ static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy)
if (device_property_read_u32(dev, "fsl,phy-tx-rise-tune-percent",
&imx_phy->tx_rise_tune))
imx_phy->tx_rise_tune = PHY_TUNE_DEFAULT;
+ else if (is_imx95)
+ imx_phy->tx_rise_tune =
+ imx95_phy_tx_rise_tune_from_property(imx_phy->tx_rise_tune);
else
imx_phy->tx_rise_tune =
phy_tx_rise_tune_from_property(imx_phy->tx_rise_tune);
@@ -172,6 +465,9 @@ static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy)
if (device_property_read_u32(dev, "fsl,phy-comp-dis-tune-percent",
&imx_phy->comp_dis_tune))
imx_phy->comp_dis_tune = PHY_TUNE_DEFAULT;
+ else if (is_imx95)
+ imx_phy->comp_dis_tune =
+ imx95_phy_comp_dis_tune_from_property(imx_phy->comp_dis_tune);
else
imx_phy->comp_dis_tune =
phy_comp_dis_tune_from_property(imx_phy->comp_dis_tune);
@@ -315,6 +611,9 @@ static int imx8mp_usb_phy_init(struct phy *phy)
imx8m_phy_tune(imx_phy);
+ if (imx_phy->tca)
+ tca_blk_init(imx_phy->tca);
+
return 0;
}
@@ -359,6 +658,8 @@ static const struct of_device_id imx8mq_usb_phy_of_match[] = {
.data = &imx8mq_usb_phy_ops,},
{.compatible = "fsl,imx8mp-usb-phy",
.data = &imx8mp_usb_phy_ops,},
+ {.compatible = "fsl,imx95-usb-phy",
+ .data = &imx8mp_usb_phy_ops,},
{ }
};
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
@@ -398,6 +699,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
phy_set_drvdata(imx_phy->phy, imx_phy);
+ imx_phy->tca = imx95_usb_phy_get_tca(pdev, imx_phy);
+ if (IS_ERR(imx_phy->tca))
+ return dev_err_probe(dev, PTR_ERR(imx_phy->tca),
+ "failed to get tca\n");
+
imx8m_get_phy_tuning_data(imx_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -405,8 +711,16 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
+static void imx8mq_usb_phy_remove(struct platform_device *pdev)
+{
+ struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
+
+ imx95_usb_phy_put_tca(imx_phy);
+}
+
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
+ .remove = imx8mq_usb_phy_remove,
.driver = {
.name = "imx8mq-usb-phy",
.of_match_table = imx8mq_usb_phy_of_match,
diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
index 45004f598e4d..191c282246d9 100644
--- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
@@ -325,7 +325,7 @@ to_fsl_samsung_hdmi_phy(struct clk_hw *hw)
return container_of(hw, struct fsl_samsung_hdmi_phy, hw);
}
-static void
+static int
fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy,
const struct phy_config *cfg)
{
@@ -341,6 +341,9 @@ fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy,
break;
}
+ if (unlikely(div == 4))
+ return -EINVAL;
+
writeb(FIELD_PREP(REG12_CK_DIV_MASK, div), phy->regs + PHY_REG(12));
/*
@@ -364,6 +367,8 @@ fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy,
FIELD_PREP(REG14_RP_CODE_MASK, 2) |
FIELD_PREP(REG14_TG_CODE_HIGH_MASK, fld_tg_code >> 8),
phy->regs + PHY_REG(14));
+
+ return 0;
}
static unsigned long fsl_samsung_hdmi_phy_find_pms(unsigned long fout, u8 *p, u16 *m, u8 *s)
@@ -451,6 +456,8 @@ static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy,
int i, ret;
u8 val;
+ phy->cur_cfg = cfg;
+
/* HDMI PHY init */
writeb(REG33_FIX_DA, phy->regs + PHY_REG(33));
@@ -466,7 +473,11 @@ static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy,
writeb(REG21_SEL_TX_CK_INV | FIELD_PREP(REG21_PMS_S_MASK,
cfg->pll_div_regs[2] >> 4), phy->regs + PHY_REG(21));
- fsl_samsung_hdmi_phy_configure_pll_lock_det(phy, cfg);
+ ret = fsl_samsung_hdmi_phy_configure_pll_lock_det(phy, cfg);
+ if (ret) {
+ dev_err(phy->dev, "pixclock too large\n");
+ return ret;
+ }
writeb(REG33_FIX_DA | REG33_MODE_SET_DONE, phy->regs + PHY_REG(33));
@@ -499,7 +510,14 @@ static const struct phy_config *fsl_samsung_hdmi_phy_lookup_rate(unsigned long r
if (phy_pll_cfg[i].pixclk <= rate)
break;
- return &phy_pll_cfg[i];
+ /* If there is an exact match, or the array has been searched, return the value*/
+ if (phy_pll_cfg[i].pixclk == rate || i + 1 > ARRAY_SIZE(phy_pll_cfg) - 1)
+ return &phy_pll_cfg[i];
+
+ /* See if the next entry is closer to nominal than this one */
+ return (abs((long) rate - (long) phy_pll_cfg[i].pixclk) <
+ abs((long) rate - (long) phy_pll_cfg[i+1].pixclk) ?
+ &phy_pll_cfg[i] : &phy_pll_cfg[i+1]);
}
static void fsl_samsung_hdmi_calculate_phy(struct phy_config *cal_phy, unsigned long rate,
@@ -512,18 +530,9 @@ static void fsl_samsung_hdmi_calculate_phy(struct phy_config *cal_phy, unsigned
/* pll_div_regs 3-6 are fixed and pre-defined already */
}
-static u32 fsl_samsung_hdmi_phy_get_closest_rate(unsigned long rate,
- u32 int_div_clk, u32 frac_div_clk)
-{
- /* Calculate the absolute value of the differences and return whichever is closest */
- if (abs((long)rate - (long)int_div_clk) < abs((long)(rate - (long)frac_div_clk)))
- return int_div_clk;
-
- return frac_div_clk;
-}
-
-static long phy_clk_round_rate(struct clk_hw *hw,
- unsigned long rate, unsigned long *parent_rate)
+static
+const struct phy_config *fsl_samsung_hdmi_phy_find_settings(struct fsl_samsung_hdmi_phy *phy,
+ unsigned long rate)
{
const struct phy_config *fract_div_phy;
u32 int_div_clk;
@@ -532,83 +541,66 @@ static long phy_clk_round_rate(struct clk_hw *hw,
/* If the clock is out of range return error instead of searching */
if (rate > 297000000 || rate < 22250000)
- return -EINVAL;
+ return NULL;
/* Search the fractional divider lookup table */
fract_div_phy = fsl_samsung_hdmi_phy_lookup_rate(rate);
+ if (fract_div_phy->pixclk == rate) {
+ dev_dbg(phy->dev, "fractional divider match = %u\n", fract_div_phy->pixclk);
+ return fract_div_phy;
+ }
- /* If the rate is an exact match, return that value */
- if (rate == fract_div_phy->pixclk)
- return fract_div_phy->pixclk;
-
- /* If the exact match isn't found, calculate the integer divider */
+ /* Calculate the integer divider */
int_div_clk = fsl_samsung_hdmi_phy_find_pms(rate, &p, &m, &s);
+ fsl_samsung_hdmi_calculate_phy(&calculated_phy_pll_cfg, int_div_clk, p, m, s);
+ if (int_div_clk == rate) {
+ dev_dbg(phy->dev, "integer divider match = %u\n", calculated_phy_pll_cfg.pixclk);
+ return &calculated_phy_pll_cfg;
+ }
- /* If the int_div_clk rate is an exact match, return that value */
- if (int_div_clk == rate)
- return int_div_clk;
+ /* Calculate the absolute value of the differences and return whichever is closest */
+ if (abs((long)rate - (long)int_div_clk) <
+ abs((long)rate - (long)fract_div_phy->pixclk)) {
+ dev_dbg(phy->dev, "integer divider = %u\n", calculated_phy_pll_cfg.pixclk);
+ return &calculated_phy_pll_cfg;
+ }
- /* If neither rate is an exact match, use the value from the LUT */
- return fract_div_phy->pixclk;
-}
+ dev_dbg(phy->dev, "fractional divider = %u\n", phy->cur_cfg->pixclk);
-static int phy_use_fract_div(struct fsl_samsung_hdmi_phy *phy, const struct phy_config *fract_div_phy)
-{
- phy->cur_cfg = fract_div_phy;
- dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: using fractional divider rate = %u\n",
- phy->cur_cfg->pixclk);
- return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
+ return fract_div_phy;
}
-static int phy_use_integer_div(struct fsl_samsung_hdmi_phy *phy,
- const struct phy_config *int_div_clk)
+static long fsl_samsung_hdmi_phy_clk_round_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long *parent_rate)
{
- phy->cur_cfg = &calculated_phy_pll_cfg;
- dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: integer divider rate = %u\n",
- phy->cur_cfg->pixclk);
- return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
+ struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
+ const struct phy_config *target_settings = fsl_samsung_hdmi_phy_find_settings(phy, rate);
+
+ if (target_settings == NULL)
+ return -EINVAL;
+
+ dev_dbg(phy->dev, "round_rate, closest rate = %u\n", target_settings->pixclk);
+ return target_settings->pixclk;
}
-static int phy_clk_set_rate(struct clk_hw *hw,
+static int fsl_samsung_hdmi_phy_clk_set_rate(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate)
{
struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
- const struct phy_config *fract_div_phy;
- u32 int_div_clk;
- u16 m;
- u8 p, s;
+ const struct phy_config *target_settings = fsl_samsung_hdmi_phy_find_settings(phy, rate);
- /* Search the fractional divider lookup table */
- fract_div_phy = fsl_samsung_hdmi_phy_lookup_rate(rate);
-
- /* If the rate is an exact match, use that value */
- if (fract_div_phy->pixclk == rate)
- return phy_use_fract_div(phy, fract_div_phy);
+ if (target_settings == NULL)
+ return -EINVAL;
- /*
- * If the rate from the fractional divider is not exact, check the integer divider,
- * and use it if that value is an exact match.
- */
- int_div_clk = fsl_samsung_hdmi_phy_find_pms(rate, &p, &m, &s);
- fsl_samsung_hdmi_calculate_phy(&calculated_phy_pll_cfg, int_div_clk, p, m, s);
- if (int_div_clk == rate)
- return phy_use_integer_div(phy, &calculated_phy_pll_cfg);
+ dev_dbg(phy->dev, "set_rate, closest rate = %u\n", target_settings->pixclk);
- /*
- * Compare the difference between the integer clock and the fractional clock against
- * the desired clock and which whichever is closest.
- */
- if (fsl_samsung_hdmi_phy_get_closest_rate(rate, int_div_clk,
- fract_div_phy->pixclk) == fract_div_phy->pixclk)
- return phy_use_fract_div(phy, fract_div_phy);
- else
- return phy_use_integer_div(phy, &calculated_phy_pll_cfg);
+ return fsl_samsung_hdmi_phy_configure(phy, target_settings);
}
static const struct clk_ops phy_clk_ops = {
.recalc_rate = phy_clk_recalc_rate,
- .round_rate = phy_clk_round_rate,
- .set_rate = phy_clk_set_rate,
+ .round_rate = fsl_samsung_hdmi_phy_clk_round_rate,
+ .set_rate = fsl_samsung_hdmi_phy_clk_set_rate,
};
static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
@@ -659,7 +651,7 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
if (IS_ERR(phy->regs))
return PTR_ERR(phy->regs);
- phy->apbclk = devm_clk_get(phy->dev, "apb");
+ phy->apbclk = devm_clk_get_enabled(phy->dev, "apb");
if (IS_ERR(phy->apbclk))
return dev_err_probe(phy->dev, PTR_ERR(phy->apbclk),
"failed to get apb clk\n");
@@ -669,12 +661,6 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
return dev_err_probe(phy->dev, PTR_ERR(phy->refclk),
"failed to get ref clk\n");
- ret = clk_prepare_enable(phy->apbclk);
- if (ret) {
- dev_err(phy->dev, "failed to enable apbclk\n");
- return ret;
- }
-
pm_runtime_get_noresume(phy->dev);
pm_runtime_set_active(phy->dev);
pm_runtime_enable(phy->dev);
@@ -690,8 +676,6 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
return 0;
register_clk_failed:
- clk_disable_unprepare(phy->apbclk);
-
return ret;
}