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 <stelf...@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