Re: [PATCH v2 3/3] PCI: imx6: Add support for i.MX6 PCIe controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Hi Sean,

Several comments inline.


On Tue, Sep 10, 2013 at 04:11:15AM +0000, Sean Cross wrote:
> +
> +#define PHY_RX_OVRD_IN_LO 0x1005
> +#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1<<5)
> +#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1<<3)
> +
> +
> +

Remove all duplicate empty lines please.

> +static void imx_iomuxc_clrset(struct imx6_pcie *imx6_pcie,
> +			    u32 mask, u32 val, u32 reg)
> +{
> +	u32 tmp;
> +	regmap_read(imx6_pcie->iomuxc_gpr, reg, &tmp);
> +	tmp &= ~mask;
> +	tmp |= (val & mask);
> +	regmap_write(imx6_pcie->iomuxc_gpr, reg, tmp);
> +}

We have regmap_update_bits. Use it.

> +
> +
> +static int pcie_phy_ack_polling(void __iomem *dbi_base, int max_iterations,
> +				   int exp_val)
> +{
> +	u32 temp_rd_data;
> +	u32 wait_counter = 0;
> +
> +	do {
> +		temp_rd_data = readl(dbi_base + PCIE_PHY_STAT);
> +		temp_rd_data = (temp_rd_data >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
> +		wait_counter++;
> +	} while ((wait_counter < max_iterations) && (temp_rd_data != exp_val));
> +
> +	if (temp_rd_data != exp_val)
> +		return 0;
> +
> +	return 1;
> +}

You should return 0 for success and a negative error value on error.

> +
> +
> +static int pcie_phy_cap_addr(void __iomem *dbi_base, int addr)
> +{
> +	u32 temp_wr_data;
> +
> +	/* write addr */
> +	temp_wr_data = addr << PCIE_PHY_CTRL_DATA_LOC;
> +	writel(temp_wr_data, dbi_base + PCIE_PHY_CTRL);
> +
> +	/* capture addr */
> +	temp_wr_data |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
> +	writel(temp_wr_data, dbi_base + PCIE_PHY_CTRL);
> +
> +	/* wait for ack */
> +	if (!pcie_phy_ack_polling(dbi_base, 100, 1))
> +		return 0;

Drop these comments and give the function a more meaningful name, like
pcie_phy_wait_ack().

> +
> +	/* deassert cap addr */
> +	temp_wr_data = addr << PCIE_PHY_CTRL_DATA_LOC;
> +	writel(temp_wr_data, dbi_base + PCIE_PHY_CTRL);
> +
> +	/* wait for ack de-assetion */
> +	if (!pcie_phy_ack_polling(dbi_base, 100, 0))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +
> +
> +/* Read from the 16-bt PCIe PHY control registers (not memory-mapped) */
> +static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
> +{
> +	u32 temp_rd_data, temp_wr_data;

no_need_for_so_long_var_names. 'val' or similar will do it.

> +
> +	/* cap addr */
> +	if (!pcie_phy_cap_addr(dbi_base, addr))
> +		return 0;
> +
> +	/* assert rd signal */
> +	temp_wr_data = 0x1 << PCIE_PHY_CTRL_RD_LOC;
> +	writel(temp_wr_data, dbi_base + PCIE_PHY_CTRL);
> +
> +	/* wait for ack */
> +	if (!pcie_phy_ack_polling(dbi_base, 100, 1))
> +		return 0;
> +
> +	/* after got ack return data */
> +	temp_rd_data = readl(dbi_base + PCIE_PHY_STAT);
> +	*data = (temp_rd_data & (0xffff << PCIE_PHY_STAT_DATA_LOC)) ;
> +
> +	/* deassert rd signal */
> +	temp_wr_data = 0x0;
> +	writel(temp_wr_data, dbi_base + PCIE_PHY_CTRL);

Why not writel(0x0, ...). It's shorter and easier to read.

> +
> +	/* wait for ack de-assetion */
> +	if (!pcie_phy_ack_polling(dbi_base, 100, 0))
> +		return 0;
> +
> +	return 1;
> +}
> +

...

> +static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
> +{
> +	int ret;
> +	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
> +
> +	if (imx6_pcie->power_on_gpio >= 0)
> +		gpio_set_value(imx6_pcie->power_on_gpio, IMX6_POWER_ON);
> +
> +        imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR1_PCIE_TEST_PD,
> +			0 << 18, IOMUXC_GPR1);
> +        imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR1_PCIE_REF_CLK_EN,
> +			1 << 16, IOMUXC_GPR1);

Indentation broken here.

> +
> +        /* Enable clocks */
> +        ret = clk_set_parent(imx6_pcie->lvds1_sel, imx6_pcie->sata_ref);
> +        if (ret) {
> +                dev_err(pp->dev, "unable to set lvds1_gate parent\n");
> +                return ret;
> +        }
> +
> +        ret = clk_prepare_enable(imx6_pcie->pcie_ref_125m);
> +        if (ret) {
> +                dev_err(pp->dev, "unable to enable pcie_ref_125m\n");
> +                return ret;
> +        }
> +
> +        ret = clk_prepare_enable(imx6_pcie->lvds1_gate);
> +        if (ret) {
> +                dev_err(pp->dev, "unable to enable lvds1_gate\n");
> +                return ret;
> +        }
> +
> +        ret = clk_prepare_enable(imx6_pcie->pcie_axi);
> +        if (ret) {
> +                dev_err(pp->dev, "unable to enable pcie_axi\n");
> +                return ret;
> +        }

clock enable/disable must be balanced.

> +
> +	/* allow the clocks to stabilize */
> +	usleep_range(100, 200);
> +
> +	return 0;
> +}
> +
> +static void imx6_pcie_init_phy(struct pcie_port *pp)
> +{
> +	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
> +
> +	/* FIXME the field name should be aligned to RM */
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR12_PCIE_CTL_2,
> +			0 << 10, IOMUXC_GPR12);
> +
> +	/* configure constant input signal to the pcie ctrl and phy */
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR12_DEVICE_TYPE,
> +			PCI_EXP_TYPE_ROOT_PORT << 12, IOMUXC_GPR12);
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR12_LOS_LEVEL,
> +			9 << 4, IOMUXC_GPR12);
> +
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR8_TX_DEEMPH_GEN1,
> +			0 << 0, IOMUXC_GPR8);
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
> +			0 << 6, IOMUXC_GPR8);
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
> +			20 << 12, IOMUXC_GPR8);
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR8_TX_SWING_FULL,
> +			127 << 18, IOMUXC_GPR8);
> +	imx_iomuxc_clrset(imx6_pcie, IMX6Q_GPR8_TX_SWING_LOW,
> +			127 << 25, IOMUXC_GPR8);
> +}
> +
> +static int imx6_pcie_establish_link(struct pcie_port *pp)
> +{
> +	int count = 0;
> +	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
> +
> +	/* assert reset signals */
> +	imx6_pcie_assert_core_reset(pp);

Please drop

> +
> +	/* initialize phy */
> +	imx6_pcie_init_phy(pp);

all these

> +
> +	/* de-assert core reset */
> +	imx6_pcie_deassert_core_reset(pp);

useless

> +
> +	/* setup root complex */
> +	dw_pcie_setup_rc(pp);

comments. I mean when I read a book I usually don't wear a sign on my
head saying 'I'm reading a book'

> +	/* From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
> +	 * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
> +	 * If (MAC/LTSSM.state == Recovery.RcvrLock)
> +	 * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
> +	 * to gen2 is stuck
> +	 */
> +	pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT,
> +			 &rx_valid);

	if (rx_valid & 0x1)
		return 0;

> +	ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F;

	if (ltssm != 0xd)
		return 0;

	/* reset phy here */


> +	if ((ltssm == 0x0D) && ((rx_valid & 0x01) == 0)) {
> +		dev_err(pp->dev,
> +			"transition to gen2 is stuck, reset PHY!\n");
> +
> +		pcie_phy_read(pp->dbi_base,
> +			PHY_RX_OVRD_IN_LO, &temp);
> +		temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN
> +			| PHY_RX_OVRD_IN_LO_RX_PLL_EN);
> +		pcie_phy_write(pp->dbi_base,
> +			PHY_RX_OVRD_IN_LO, temp);
> +
> +		usleep_range(2000, 3000);
> +
> +		pcie_phy_read(pp->dbi_base,
> +			PHY_RX_OVRD_IN_LO, &temp);
> +		temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN
> +		        | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
> +		pcie_phy_write(pp->dbi_base,
> +			PHY_RX_OVRD_IN_LO, temp);
> +	}
> +	return 0;
> +}
> +
> +static void imx6_pcie_host_init(struct pcie_port *pp)
> +{
> +	imx6_pcie_establish_link(pp);
> +}

rename imx6_pcie_establish_link to imx6_pcie_host_init and drop this
function.

> +static int __init imx6_pcie_probe(struct platform_device *pdev)
> +{
> +	struct imx6_pcie *imx6_pcie;
> +	struct pcie_port *pp;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct resource *dbi_base;
> +	int ret;
> +
> +	imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie),
> +				GFP_KERNEL);
> +	if (!imx6_pcie) {
> +		dev_err(&pdev->dev, "no memory for imx6 pcie\n");

Drop this message. You'll never see it anyway.

> +		return -ENOMEM;
> +	}
> +

...

> +
> +	/* Fetch GPIOs */
> +	imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
> +	if (gpio_is_valid(imx6_pcie->reset_gpio))
> +		devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
> +				    GPIOF_OUT_INIT_LOW,
> +				    "PCIe reset");

devm_gpio_request_one can fail.

> +fail_add_port:
> +fail_get_gpr:
> +	clk_disable_unprepare(imx6_pcie->sata_ref);
> +fail_sata_ref_clk:
> +	clk_disable_unprepare(imx6_pcie->pcie_axi);
> +fail_pcie_axi_clk:
> +	clk_disable_unprepare(imx6_pcie->pcie_ref_125m);
> +fail_pcie_ref_125m_clk:
> +	clk_disable_unprepare(imx6_pcie->lvds1_gate);
> +fail_lvds_clk:
> +	clk_disable_unprepare(imx6_pcie->lvds1_sel);

Why do you disable clocks you never enabled before?

> +static int __exit imx6_pcie_remove(struct platform_device *pdev)
> +{
> +	struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev);
> +
> +	clk_disable_unprepare(imx6_pcie->pcie_axi);
> +	clk_disable_unprepare(imx6_pcie->lvds1_gate);
> +

The choice of clocks you disable seems rather arbitrary. Also I would
assume that you deregister the pcie controller here.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux