lkml.org 
[lkml]   [2018]   [Jul]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
SubjectRe: [PATCH v2] phy: Add driver for Cadence MHDP DisplayPort SD0801 PHY
From
Date
Hi,

On Friday 20 July 2018 06:11 PM, Scott Telford wrote:
> Add driver for the Cadence SD0801 "Torrent" PHY used with the Cadence MHDP
> DisplayPort Tx controller.
>
> Integration with the MHDP driver will be the subject of another commit.
>
> Signed-off-by: Scott Telford <stelford@cadence.com>
> ---
> .../devicetree/bindings/phy/phy-cadence-dp.txt | 28 ++
> drivers/phy/Kconfig | 1 +
> drivers/phy/Makefile | 1 +
> drivers/phy/cadence/Kconfig | 10 +
> drivers/phy/cadence/Makefile | 1 +
> drivers/phy/cadence/phy-cadence-dp.c | 436 +++++++++++++++++++++
> drivers/phy/cadence/phy-cadence-dp.h | 117 ++++++
> 7 files changed, 594 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
> create mode 100644 drivers/phy/cadence/Kconfig
> create mode 100644 drivers/phy/cadence/Makefile
> create mode 100644 drivers/phy/cadence/phy-cadence-dp.c
> create mode 100644 drivers/phy/cadence/phy-cadence-dp.h
>
> diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-dp.txt b/Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
> new file mode 100644
> index 0000000..8de0526
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
> @@ -0,0 +1,28 @@
> +Cadence MHDP DisplayPort SD0801 PHY binding
> +===========================================
> +
> +This binding describes the Cadence SD0801 PHY hardware included with
> +the Cadence MHDP DisplayPort controller.
> +
> +-------------------------------------------------------------------------------
> +Required properties (controller (parent) node):
> +- compatible : Should be "cdns,dp-phy"
> +- reg : Defines the following sets of registers in the parent
> + mhdp device:
> + - Offset of the DPTX PHY configuration registers
> + - Offset of the SD0801 PHY configuration registers
> +- num_lanes : Number of DisplayPort lanes to use (1, 2 or 4)
> +- max_bit_rate : Maximum DisplayPort link bit rate to use, in Mbps (2160,
> + 2430, 2700, 3240, 4320, 5400 or 8100)
> +- #phy-cells : from the generic PHY bindings, must be 0.
> +-------------------------------------------------------------------------------
> +
> +Example:
> + dp_phy: phy@f0fb030a00 {
> + compatible = "cdns,dp-phy";
> + reg = <0xf0 0xfb030a00 0x0 0x00000040>,
> + <0xf0 0xfb500000 0x0 0x00100000>;
> + num_lanes = <4>;
> + max_bit_rate = <8100>;
> + #phy-cells = <0>;
> + };

dt bindings should be a separate patch
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 5c8d452..cc47f85 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -43,6 +43,7 @@ config PHY_XGENE
> source "drivers/phy/allwinner/Kconfig"
> source "drivers/phy/amlogic/Kconfig"
> source "drivers/phy/broadcom/Kconfig"
> +source "drivers/phy/cadence/Kconfig"
> source "drivers/phy/hisilicon/Kconfig"
> source "drivers/phy/lantiq/Kconfig"
> source "drivers/phy/marvell/Kconfig"
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 84e3bd9..ba48acd 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -15,6 +15,7 @@ obj-$(CONFIG_ARCH_RENESAS) += renesas/
> obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
> obj-$(CONFIG_ARCH_TEGRA) += tegra/
> obj-y += broadcom/ \
> + cadence/ \
> hisilicon/ \
> marvell/ \
> motorola/ \
> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
> new file mode 100644
> index 0000000..57fff7d
> --- /dev/null
> +++ b/drivers/phy/cadence/Kconfig
> @@ -0,0 +1,10 @@
> +#
> +# Phy driver for Cadence MHDP DisplayPort controller
> +#
> +config PHY_CADENCE_DP
> + tristate "Cadence MHDP DisplayPort PHY driver"
> + depends on OF
> + depends on HAS_IOMEM
> + select GENERIC_PHY
> + help
> + Support for Cadence MHDP DisplayPort PHY.
> diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
> new file mode 100644
> index 0000000..e5b0a11
> --- /dev/null
> +++ b/drivers/phy/cadence/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o
> diff --git a/drivers/phy/cadence/phy-cadence-dp.c b/drivers/phy/cadence/phy-cadence-dp.c
> new file mode 100644
> index 0000000..c1cf89b
> --- /dev/null
> +++ b/drivers/phy/cadence/phy-cadence-dp.c
> @@ -0,0 +1,436 @@
> +/*
> + * Cadence MHDP DisplayPort SD0801 PHY driver.
> + *
> + * Copyright 2018 Cadence Design Systems, Inc.
> + *
> + * SPDX-License-Identifier: GPL-2.0-only
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include "phy-cadence-dp.h"

If phy-cadence-dp.h is not going to be used in multiple files, then all the
definitions should be added here and the .h can removed.
> +
> +
> +static const struct phy_ops cdns_dp_phy_ops = {
> + .init = cdns_dp_phy_init,
> + .owner = THIS_MODULE,
> +};
> +
> +static const struct of_device_id cdns_dp_phy_of_match[] = {
> + {
> + .compatible = "cdns,dp-phy"
> + },
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(of, cdns_dp_phy_of_match);
> +
> +static int cdns_dp_phy_probe(struct platform_device *pdev)

The file organization is slightly different. Can we follow what other drivers
do by having the probe in the bottom of the file and other functions above that?
> +{
> + struct resource *regs;
> + struct cdns_dp_phy *cdns_phy;
> + struct device *dev = &pdev->dev;
> + struct phy_provider *phy_provider;
> + struct phy *phy;
> + int err;
> +
> + cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
> + if (!cdns_phy)
> + return -ENOMEM;
> +
> + cdns_phy->dev = &pdev->dev;
> +
> + phy = devm_phy_create(dev, NULL, &cdns_dp_phy_ops);
> + if (IS_ERR(phy)) {
> + dev_err(dev, "failed to create DisplayPort PHY\n");
> + return PTR_ERR(phy);
> + }
> +
> + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs);
> + if (IS_ERR(cdns_phy->base))
> + return PTR_ERR(cdns_phy->base);
> +
> + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
> + if (IS_ERR(cdns_phy->sd_base))
> + return PTR_ERR(cdns_phy->sd_base);
> +
> + err = device_property_read_u32(dev, "num_lanes",
> + &(cdns_phy->num_lanes));
> + if (err)
> + cdns_phy->num_lanes = DEFAULT_NUM_LANES;
> +
> + switch (cdns_phy->num_lanes) {
> + case 1:
> + case 2:
> + case 4:
> + /* valid number of lanes */
> + break;
> + default:
> + dev_err(dev, "unsupported number of lanes: %d\n",
> + cdns_phy->num_lanes);
> + return -EINVAL;
> + }
> +
> + err = device_property_read_u32(dev, "max_bit_rate",
> + &(cdns_phy->max_bit_rate));
> + if (err)
> + cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
> +
> + switch (cdns_phy->max_bit_rate) {
> + case 2160:
> + case 2430:
> + case 2700:
> + case 3240:
> + case 4320:
> + case 5400:
> + case 8100:
> + /* valid bit rate */
> + break;
> + default:
> + dev_err(dev, "unsupported max bit rate: %dMbps\n",
> + cdns_phy->max_bit_rate);
> + return -EINVAL;
> + }
> +
> + phy_set_drvdata(phy, cdns_phy);
> +
> + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +
> + dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
> + cdns_phy->num_lanes,
> + cdns_phy->max_bit_rate / 1000,
> + cdns_phy->max_bit_rate % 1000);
> +
> + return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +
> +static int cdns_dp_phy_init(struct phy *phy)
> +{
> + unsigned char lane_bits;
> +
> + struct cdns_dp_phy *cdns_phy = phy_get_drvdata(phy);
> +
> + writel(0x0003, cdns_phy->base + PHY_AUX_CTRL); /* enable AUX */
> +
> + /* PHY PMA registers configuration function */
> + cdns_dp_phy_pma_cfg(cdns_phy);
> +
> + /* Set lines power state to A0
> + * Set lines pll clk enable to 0
> + */
> +
> + cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ,
> + PHY_POWER_STATE_LN_0, 6, 0x0000);
> +
> + if (cdns_phy->num_lanes >= 2) {
> + cdns_dp_phy_write_field(cdns_phy,
> + PHY_PMA_XCVR_POWER_STATE_REQ,
> + PHY_POWER_STATE_LN_1, 6, 0x0000);
> +
> + if (cdns_phy->num_lanes == 4) {
> + cdns_dp_phy_write_field(cdns_phy,
> + PHY_PMA_XCVR_POWER_STATE_REQ,
> + PHY_POWER_STATE_LN_2, 6, 0);
> + cdns_dp_phy_write_field(cdns_phy,
> + PHY_PMA_XCVR_POWER_STATE_REQ,
> + PHY_POWER_STATE_LN_3, 6, 0);
> + }
> + }
> +
> + cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
> + 0, 1, 0x0000);
> +
> + if (cdns_phy->num_lanes >= 2) {
> + cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
> + 1, 1, 0x0000);
> + if (cdns_phy->num_lanes == 4) {
> + cdns_dp_phy_write_field(cdns_phy,
> + PHY_PMA_XCVR_PLLCLK_EN,
> + 2, 1, 0x0000);
> + cdns_dp_phy_write_field(cdns_phy,
> + PHY_PMA_XCVR_PLLCLK_EN,
> + 3, 1, 0x0000);
> + }
> + }
> +
> + /* release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
> + * used lanes
> + */
> + lane_bits = (1 << cdns_phy->num_lanes) - 1;
> + writel(((0xF & ~lane_bits) << 4) | (0xF & lane_bits),
> + cdns_phy->base + PHY_RESET);
> +
> + /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
> + writel(0x0001, cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN);
> +
> + /* PHY PMA registers configuration functions */
> + cdns_dp_phy_pma_cmn_vco_cfg_25mhz(cdns_phy);
> + cdns_dp_phy_pma_cmn_rate(cdns_phy);
> +
> + /* take out of reset */
> + cdns_dp_phy_write_field(cdns_phy, PHY_RESET, 8, 1, 1);
> + cdns_dp_phy_wait_pma_cmn_ready(cdns_phy);
> + cdns_dp_phy_run(cdns_phy);
> +
> + return 0;
> +}
> +
> +static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy)
> +{
> + unsigned int reg;
> + int ret;
> +
> + ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_CMN_READY, reg,
> + reg & 1, 0, 500);
> + if (ret == -ETIMEDOUT)
> + dev_err(cdns_phy->dev,
> + "timeout waiting for PMA common ready\n");
> +}
> +
> +static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy)
> +{
> + unsigned int i;
> +
> + /* PMA common configuration */
> + cdns_dp_phy_pma_cmn_cfg_25mhz(cdns_phy);
> +
> + /* PMA lane configuration to deal with multi-link operation */
> + for (i = 0; i < cdns_phy->num_lanes; i++)
> + cdns_dp_phy_pma_lane_cfg(cdns_phy, i);
> +}
> +
> +static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
> +{
> + /* refclock registers - assumes 25 MHz refclock */
> + writel(0x0019, cdns_phy->sd_base + CMN_SSM_BIAS_TMR);
> + writel(0x0032, cdns_phy->sd_base + CMN_PLLSM0_PLLPRE_TMR);
> + writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM0_PLLLOCK_TMR);
> + writel(0x0032, cdns_phy->sd_base + CMN_PLLSM1_PLLPRE_TMR);
> + writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM1_PLLLOCK_TMR);
> + writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_INIT_TMR);
> + writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_ITER_TMR);
> + writel(0x0019, cdns_phy->sd_base + CMN_IBCAL_INIT_TMR);
> + writel(0x001E, cdns_phy->sd_base + CMN_TXPUCAL_INIT_TMR);
> + writel(0x0006, cdns_phy->sd_base + CMN_TXPUCAL_ITER_TMR);
> + writel(0x001E, cdns_phy->sd_base + CMN_TXPDCAL_INIT_TMR);
> + writel(0x0006, cdns_phy->sd_base + CMN_TXPDCAL_ITER_TMR);
> + writel(0x02EE, cdns_phy->sd_base + CMN_RXCAL_INIT_TMR);
> + writel(0x0006, cdns_phy->sd_base + CMN_RXCAL_ITER_TMR);
> + writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_INIT_TMR);
> + writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_ITER_TMR);
> + writel(0x000E, cdns_phy->sd_base + CMN_SD_CAL_REFTIM_START);
> + writel(0x012B, cdns_phy->sd_base + CMN_SD_CAL_PLLCNT_START);
> + /* PLL registers */
> + writel(0x0409, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_PADJ_M0);
> + writel(0x1001, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_IADJ_M0);
> + writel(0x0F08, cdns_phy->sd_base + CMN_PDIAG_PLL0_FILT_PADJ_M0);
> + writel(0x0004, cdns_phy->sd_base + CMN_PLL0_DSM_DIAG_M0);
> + writel(0x00FA, cdns_phy->sd_base + CMN_PLL0_VCOCAL_INIT_TMR);
> + writel(0x0004, cdns_phy->sd_base + CMN_PLL0_VCOCAL_ITER_TMR);
> + writel(0x00FA, cdns_phy->sd_base + CMN_PLL1_VCOCAL_INIT_TMR);
> + writel(0x0004, cdns_phy->sd_base + CMN_PLL1_VCOCAL_ITER_TMR);
> + writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_REFTIM_START);

Are these values that are written just calibration data or are they bitfields
for a specific setting. If they are not calibration data, then macros should be
added.
> +}
> +
> +static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
> +{
> + /* Assumes 25 MHz refclock */
> + switch (cdns_phy->max_bit_rate) {
> + /* Setting VCO for 10.8GHz */
> + case 2700:
> + case 5400:
> + writel(0x01B0, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
> + writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
> + writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
> + writel(0x0120, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
> + break;
> + /* Setting VCO for 9.72GHz */
> + case 2430:
> + case 3240:
> + writel(0x0184, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
> + writel(0xCCCD, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
> + writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
> + writel(0x0104, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
> + break;
> + /* Setting VCO for 8.64GHz */
> + case 2160:
> + case 4320:
> + writel(0x0159, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
> + writel(0x999A, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
> + writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
> + writel(0x00E7, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
> + break;
> + /* Setting VCO for 8.1GHz */
> + case 8100:
> + writel(0x0144, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
> + writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
> + writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
> + writel(0x00D8, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
> + break;
> + }
> +
> + writel(0x0002, cdns_phy->sd_base + CMN_PDIAG_PLL0_CTRL_M0);
> + writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_PLLCNT_START);

same for this function also.
> +}
> +
> +static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy)
> +{
> + unsigned int clk_sel_val = 0;
> + unsigned int hsclk_div_val = 0;
> + unsigned int i;
> +
> + /* 16'h0000 for single DP link configuration */
> + writel(0x0000, cdns_phy->sd_base + PHY_PLL_CFG);
> +
> + switch (cdns_phy->max_bit_rate) {
> + case 1620:
> + clk_sel_val = 0x0f01;
> + hsclk_div_val = 2;
> + break;
> + case 2160:
> + case 2430:
> + case 2700:
> + clk_sel_val = 0x0701;
> + hsclk_div_val = 1;
> + break;
> + case 3240:
> + clk_sel_val = 0x0b00;
> + hsclk_div_val = 2;
> + break;
> + case 4320:
> + case 5400:
> + clk_sel_val = 0x0301;
> + hsclk_div_val = 0;
> + break;
> + case 8100:
> + clk_sel_val = 0x0200;
> + hsclk_div_val = 0;
> + break;
> + }
> +
> + writel(clk_sel_val, cdns_phy->sd_base + CMN_PDIAG_PLL0_CLK_SEL_M0);
> +
> + /* PMA lane configuration to deal with multi-link operation */
> + for (i = 0; i < cdns_phy->num_lanes; i++) {
> + writel(hsclk_div_val,
> + cdns_phy->sd_base + (XCVR_DIAG_HSCLK_DIV | (i<<11)));
> + }
> +}
> +
> +static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
> + unsigned int lane)
> +{
> + unsigned int i;
> +
> + i = 0x0007 & lane;

macro for mask has to be added.

Thanks
Kishon

\
 
 \ /
  Last update: 2018-07-24 05:58    [W:0.106 / U:0.520 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site