Release 4.9 drivers/pci/host/pci-imx6.c
  
  
/*
 * PCIe host controller driver for Freescale i.MX6 SoCs
 *
 * Copyright (C) 2013 Kosagi
 *              http://www.kosagi.com
 *
 * Author: Sean Cross <xobs@kosagi.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/resource.h>
#include <linux/signal.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include "pcie-designware.h"
#define to_imx6_pcie(x)	container_of(x, struct imx6_pcie, pp)
enum imx6_pcie_variants {
	
IMX6Q,
	
IMX6SX,
	
IMX6QP,
};
struct imx6_pcie {
	
struct pcie_port	pp;	/* pp.dbi_base is DT 0th resource */
	
int			reset_gpio;
	
bool			gpio_active_high;
	
struct clk		*pcie_bus;
	
struct clk		*pcie_phy;
	
struct clk		*pcie_inbound_axi;
	
struct clk		*pcie;
	
struct regmap		*iomuxc_gpr;
	
enum imx6_pcie_variants variant;
	
u32			tx_deemph_gen1;
	
u32			tx_deemph_gen2_3p5db;
	
u32			tx_deemph_gen2_6db;
	
u32			tx_swing_full;
	
u32			tx_swing_low;
	
int			link_gen;
};
/* PCIe Root Complex registers (memory-mapped) */
#define PCIE_RC_LCR				0x7c
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1	0x1
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2	0x2
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK	0xf
#define PCIE_RC_LCSR				0x80
/* PCIe Port Logic registers (memory-mapped) */
#define PL_OFFSET 0x700
#define PCIE_PL_PFLR (PL_OFFSET + 0x08)
#define PCIE_PL_PFLR_LINK_STATE_MASK		(0x3f << 16)
#define PCIE_PL_PFLR_FORCE_LINK			(1 << 15)
#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING	(1 << 29)
#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP		(1 << 4)
#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
#define PCIE_PHY_CTRL_DATA_LOC 0
#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
#define PCIE_PHY_CTRL_WR_LOC 18
#define PCIE_PHY_CTRL_RD_LOC 19
#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
#define PCIE_PHY_STAT_ACK_LOC 16
#define PCIE_LINK_WIDTH_SPEED_CONTROL	0x80C
#define PORT_LOGIC_SPEED_CHANGE		(0x1 << 17)
/* PHY registers (not memory-mapped) */
#define PCIE_PHY_RX_ASIC_OUT 0x100D
#define PCIE_PHY_RX_ASIC_OUT_VALID	(1 << 0)
#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)
static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	u32 val;
	u32 max_iterations = 10;
	u32 wait_counter = 0;
	do {
		val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
		val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
		wait_counter++;
		if (val == exp_val)
			return 0;
		udelay(1);
	} while (wait_counter < max_iterations);
	return -ETIMEDOUT;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 71 | 81.61% | 1 | 33.33% | 
| bjorn helgaas | bjorn helgaas | 16 | 18.39% | 2 | 66.67% | 
 | Total | 87 | 100.00% | 3 | 100.00% | 
static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	u32 val;
	int ret;
	val = addr << PCIE_PHY_CTRL_DATA_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
	val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
	ret = pcie_phy_poll_ack(imx6_pcie, 1);
	if (ret)
		return ret;
	val = addr << PCIE_PHY_CTRL_DATA_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
	return pcie_phy_poll_ack(imx6_pcie, 0);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 73 | 72.28% | 1 | 25.00% | 
| bjorn helgaas | bjorn helgaas | 27 | 26.73% | 2 | 50.00% | 
| fabio estevam | fabio estevam | 1 | 0.99% | 1 | 25.00% | 
 | Total | 101 | 100.00% | 4 | 100.00% | 
/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	u32 val, phy_ctl;
	int ret;
	ret = pcie_phy_wait_ack(imx6_pcie, addr);
	if (ret)
		return ret;
	/* assert Read signal */
	phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, phy_ctl);
	ret = pcie_phy_poll_ack(imx6_pcie, 1);
	if (ret)
		return ret;
	val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
	*data = val & 0xffff;
	/* deassert Read signal */
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x00);
	return pcie_phy_poll_ack(imx6_pcie, 0);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 90 | 76.27% | 1 | 25.00% | 
| bjorn helgaas | bjorn helgaas | 27 | 22.88% | 2 | 50.00% | 
| fabio estevam | fabio estevam | 1 | 0.85% | 1 | 25.00% | 
 | Total | 118 | 100.00% | 4 | 100.00% | 
static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	u32 var;
	int ret;
	/* write addr */
	/* cap addr */
	ret = pcie_phy_wait_ack(imx6_pcie, addr);
	if (ret)
		return ret;
	var = data << PCIE_PHY_CTRL_DATA_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
	/* capture data */
	var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
	ret = pcie_phy_poll_ack(imx6_pcie, 1);
	if (ret)
		return ret;
	/* deassert cap data */
	var = data << PCIE_PHY_CTRL_DATA_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
	/* wait for ack de-assertion */
	ret = pcie_phy_poll_ack(imx6_pcie, 0);
	if (ret)
		return ret;
	/* assert wr signal */
	var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
	/* wait for ack */
	ret = pcie_phy_poll_ack(imx6_pcie, 1);
	if (ret)
		return ret;
	/* deassert wr signal */
	var = data << PCIE_PHY_CTRL_DATA_LOC;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
	/* wait for ack de-assertion */
	ret = pcie_phy_poll_ack(imx6_pcie, 0);
	if (ret)
		return ret;
	dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x0);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 169 | 80.09% | 1 | 33.33% | 
| bjorn helgaas | bjorn helgaas | 42 | 19.91% | 2 | 66.67% | 
 | Total | 211 | 100.00% | 3 | 100.00% | 
static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
{
	u32 tmp;
	pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
	tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
		PHY_RX_OVRD_IN_LO_RX_PLL_EN);
	pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
	usleep_range(2000, 3000);
	pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
	tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
		  PHY_RX_OVRD_IN_LO_RX_PLL_EN);
	pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lucas stach | lucas stach | 67 | 88.16% | 1 | 33.33% | 
| bjorn helgaas | bjorn helgaas | 9 | 11.84% | 2 | 66.67% | 
 | Total | 76 | 100.00% | 3 | 100.00% | 
/*  Added for PCI abort handling */
static int imx6q_pcie_abort_handler(unsigned long addr,
		unsigned int fsr, struct pt_regs *regs)
{
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 22 | 100.00% | 1 | 100.00% | 
 | Total | 22 | 100.00% | 1 | 100.00% | 
static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	u32 val, gpr1, gpr12;
	switch (imx6_pcie->variant) {
	case IMX6SX:
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
				   IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
				   IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
		/* Force PCIe PHY reset */
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
				   IMX6SX_GPR5_PCIE_BTNRST_RESET,
				   IMX6SX_GPR5_PCIE_BTNRST_RESET);
		break;
	case IMX6QP:
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
				   IMX6Q_GPR1_PCIE_SW_RST,
				   IMX6Q_GPR1_PCIE_SW_RST);
		break;
	case IMX6Q:
		/*
                 * If the bootloader already enabled the link we need some
                 * special handling to get the core back into a state where
                 * it is safe to touch it for configuration.  As there is
                 * no dedicated reset signal wired up for MX6QDL, we need
                 * to manually force LTSSM into "detect" state before
                 * completely disabling LTSSM, which is a prerequisite for
                 * core configuration.
                 *
                 * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we
                 * have a strong indication that the bootloader activated
                 * the link.
                 */
		regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1);
		regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &gpr12);
		if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
		    (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
			val = dw_pcie_readl_rc(pp, PCIE_PL_PFLR);
			val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
			val |= PCIE_PL_PFLR_FORCE_LINK;
			dw_pcie_writel_rc(pp, PCIE_PL_PFLR, val);
			regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
					   IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
		}
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
				   IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
				   IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
		break;
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lucas stach | lucas stach | 83 | 41.29% | 1 | 12.50% | 
| sean cross | sean cross | 43 | 21.39% | 1 | 12.50% | 
| christoph fritz | christoph fritz | 32 | 15.92% | 1 | 12.50% | 
| andrey smirnov | andrey smirnov | 29 | 14.43% | 2 | 25.00% | 
| bjorn helgaas | bjorn helgaas | 14 | 6.97% | 3 | 37.50% | 
 | Total | 201 | 100.00% | 8 | 100.00% | 
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	struct device *dev = pp->dev;
	int ret = 0;
	switch (imx6_pcie->variant) {
	case IMX6SX:
		ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
		if (ret) {
			dev_err(dev, "unable to enable pcie_axi clock\n");
			break;
		}
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
				   IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0);
		break;
	case IMX6QP: 		/* FALLTHROUGH */
	case IMX6Q:
		/* power up core phy and enable ref clock */
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
				   IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
		/*
                 * the async reset input need ref clock to sync internally,
                 * when the ref clock comes after reset, internal synced
                 * reset time is too short, cannot meet the requirement.
                 * add one ~10us delay here.
                 */
		udelay(10);
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
				   IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
		break;
	}
	return ret;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| bjorn helgaas | bjorn helgaas | 58 | 44.27% | 2 | 40.00% | 
| christoph fritz | christoph fritz | 53 | 40.46% | 1 | 20.00% | 
| andrey smirnov | andrey smirnov | 20 | 15.27% | 2 | 40.00% | 
 | Total | 131 | 100.00% | 5 | 100.00% | 
static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	struct device *dev = pp->dev;
	int ret;
	ret = clk_prepare_enable(imx6_pcie->pcie_phy);
	if (ret) {
		dev_err(dev, "unable to enable pcie_phy clock\n");
		return;
	}
	ret = clk_prepare_enable(imx6_pcie->pcie_bus);
	if (ret) {
		dev_err(dev, "unable to enable pcie_bus clock\n");
		goto err_pcie_bus;
	}
	ret = clk_prepare_enable(imx6_pcie->pcie);
	if (ret) {
		dev_err(dev, "unable to enable pcie clock\n");
		goto err_pcie;
	}
	ret = imx6_pcie_enable_ref_clk(imx6_pcie);
	if (ret) {
		dev_err(dev, "unable to enable pcie ref clock\n");
		goto err_ref_clk;
	}
	/* allow the clocks to stabilize */
	usleep_range(200, 500);
	/* Some boards don't have PCIe reset GPIO. */
	if (gpio_is_valid(imx6_pcie->reset_gpio)) {
		gpio_set_value_cansleep(imx6_pcie->reset_gpio,
					imx6_pcie->gpio_active_high);
		msleep(100);
		gpio_set_value_cansleep(imx6_pcie->reset_gpio,
					!imx6_pcie->gpio_active_high);
	}
	switch (imx6_pcie->variant) {
	case IMX6SX:
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
				   IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
		break;
	case IMX6QP:
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
				   IMX6Q_GPR1_PCIE_SW_RST, 0);
		usleep_range(200, 500);
		break;
	case IMX6Q:		/* Nothing to do */
		break;
	}
	return;
err_ref_clk:
	clk_disable_unprepare(imx6_pcie->pcie);
err_pcie:
	clk_disable_unprepare(imx6_pcie->pcie_bus);
err_pcie_bus:
	clk_disable_unprepare(imx6_pcie->pcie_phy);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 94 | 36.29% | 1 | 7.14% | 
| bjorn helgaas | bjorn helgaas | 43 | 16.60% | 4 | 28.57% | 
| richard zhu | richard zhu | 38 | 14.67% | 2 | 14.29% | 
| andrey smirnov | andrey smirnov | 37 | 14.29% | 2 | 14.29% | 
| christoph fritz | christoph fritz | 17 | 6.56% | 1 | 7.14% | 
| lucas stach | lucas stach | 12 | 4.63% | 1 | 7.14% | 
| petr stetiar | petr stetiar | 7 | 2.70% | 1 | 7.14% | 
| tim harvey | tim harvey | 6 | 2.32% | 1 | 7.14% | 
| fabio estevam | fabio estevam | 5 | 1.93% | 1 | 7.14% | 
 | Total | 259 | 100.00% | 14 | 100.00% | 
static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
{
	if (imx6_pcie->variant == IMX6SX)
		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
				   IMX6SX_GPR12_PCIE_RX_EQ_MASK,
				   IMX6SX_GPR12_PCIE_RX_EQ_2);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
			IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
	/* configure constant input signal to the pcie ctrl and phy */
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
			IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
			   IMX6Q_GPR8_TX_DEEMPH_GEN1,
			   imx6_pcie->tx_deemph_gen1 << 0);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
			   IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
			   imx6_pcie->tx_deemph_gen2_3p5db << 6);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
			   IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
			   imx6_pcie->tx_deemph_gen2_6db << 12);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
			   IMX6Q_GPR8_TX_SWING_FULL,
			   imx6_pcie->tx_swing_full << 18);
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
			   IMX6Q_GPR8_TX_SWING_LOW,
			   imx6_pcie->tx_swing_low << 25);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 125 | 76.69% | 1 | 20.00% | 
| christoph fritz | christoph fritz | 18 | 11.04% | 1 | 20.00% | 
| justin waters | justin waters | 15 | 9.20% | 1 | 20.00% | 
| andrey smirnov | andrey smirnov | 3 | 1.84% | 1 | 20.00% | 
| bjorn helgaas | bjorn helgaas | 2 | 1.23% | 1 | 20.00% | 
 | Total | 163 | 100.00% | 5 | 100.00% | 
static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	struct device *dev = pp->dev;
	/* check if the link is up or not */
	if (!dw_pcie_wait_for_link(pp))
		return 0;
	dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
		dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
		dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
	return -ETIMEDOUT;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| bjorn helgaas | bjorn helgaas | 28 | 41.79% | 4 | 50.00% | 
| sean cross | sean cross | 27 | 40.30% | 1 | 12.50% | 
| marek vasut | marek vasut | 5 | 7.46% | 1 | 12.50% | 
| lucas stach | lucas stach | 4 | 5.97% | 1 | 12.50% | 
| joao pinto | joao pinto | 3 | 4.48% | 1 | 12.50% | 
 | Total | 67 | 100.00% | 8 | 100.00% | 
static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	struct device *dev = pp->dev;
	u32 tmp;
	unsigned int retries;
	for (retries = 0; retries < 200; retries++) {
		tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
		/* Test if the speed change finished. */
		if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
			return 0;
		usleep_range(100, 1000);
	}
	dev_err(dev, "Speed change timeout\n");
	return -EINVAL;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| troy kisky | troy kisky | 64 | 69.57% | 1 | 14.29% | 
| bjorn helgaas | bjorn helgaas | 24 | 26.09% | 4 | 57.14% | 
| marek vasut | marek vasut | 3 | 3.26% | 1 | 14.29% | 
| sean cross | sean cross | 1 | 1.09% | 1 | 14.29% | 
 | Total | 92 | 100.00% | 7 | 100.00% | 
static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
{
	struct imx6_pcie *imx6_pcie = arg;
	struct pcie_port *pp = &imx6_pcie->pp;
	return dw_handle_msi_irq(pp);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lucas stach | lucas stach | 25 | 69.44% | 1 | 50.00% | 
| bjorn helgaas | bjorn helgaas | 11 | 30.56% | 1 | 50.00% | 
 | Total | 36 | 100.00% | 2 | 100.00% | 
static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	struct device *dev = pp->dev;
	u32 tmp;
	int ret;
	/*
         * Force Gen1 operation when starting the link.  In case the link is
         * started in Gen2 mode, there is a possibility the devices on the
         * bus will not be detected at all.  This happens with PCIe switches.
         */
	tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
	tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
	tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
	dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
	/* Start LTSSM. */
	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
	ret = imx6_pcie_wait_for_link(imx6_pcie);
	if (ret) {
		dev_info(dev, "Link never came up\n");
		goto err_reset_phy;
	}
	if (imx6_pcie->link_gen == 2) {
		/* Allow Gen2 mode after the link is up. */
		tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
		tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
		tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
		dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
	} else {
		dev_info(dev, "Link: Gen2 disabled\n");
	}
	/*
         * Start Directed Speed Change so the best possible speed both link
         * partners support can be negotiated.
         */
	tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
	tmp |= PORT_LOGIC_SPEED_CHANGE;
	dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
	ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
	if (ret) {
		dev_err(dev, "Failed to bring link up!\n");
		goto err_reset_phy;
	}
	/* Make sure link training is finished as well! */
	ret = imx6_pcie_wait_for_link(imx6_pcie);
	if (ret) {
		dev_err(dev, "Failed to bring link up!\n");
		goto err_reset_phy;
	}
	tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCSR);
	dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
	return 0;
err_reset_phy:
	dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
		dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
		dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
	imx6_pcie_reset_phy(imx6_pcie);
	return ret;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| marek vasut | marek vasut | 158 | 56.63% | 2 | 18.18% | 
| bjorn helgaas | bjorn helgaas | 47 | 16.85% | 6 | 54.55% | 
| lucas stach | lucas stach | 44 | 15.77% | 1 | 9.09% | 
| tim harvey | tim harvey | 22 | 7.89% | 1 | 9.09% | 
| troy kisky | troy kisky | 8 | 2.87% | 1 | 9.09% | 
 | Total | 279 | 100.00% | 11 | 100.00% | 
static void imx6_pcie_host_init(struct pcie_port *pp)
{
	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
	imx6_pcie_assert_core_reset(imx6_pcie);
	imx6_pcie_init_phy(imx6_pcie);
	imx6_pcie_deassert_core_reset(imx6_pcie);
	dw_pcie_setup_rc(pp);
	imx6_pcie_establish_link(imx6_pcie);
	if (IS_ENABLED(CONFIG_PCI_MSI))
		dw_pcie_msi_init(pp);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| marek vasut | marek vasut | 30 | 51.72% | 1 | 20.00% | 
| bjorn helgaas | bjorn helgaas | 15 | 25.86% | 2 | 40.00% | 
| lucas stach | lucas stach | 12 | 20.69% | 1 | 20.00% | 
| sean cross | sean cross | 1 | 1.72% | 1 | 20.00% | 
 | Total | 58 | 100.00% | 5 | 100.00% | 
static int imx6_pcie_link_up(struct pcie_port *pp)
{
	return dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1) &
			PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 16 | 76.19% | 1 | 25.00% | 
| marek vasut | marek vasut | 2 | 9.52% | 1 | 25.00% | 
| bjorn helgaas | bjorn helgaas | 2 | 9.52% | 1 | 25.00% | 
| lucas stach | lucas stach | 1 | 4.76% | 1 | 25.00% | 
 | Total | 21 | 100.00% | 4 | 100.00% | 
static struct pcie_host_ops imx6_pcie_host_ops = {
	.link_up = imx6_pcie_link_up,
	.host_init = imx6_pcie_host_init,
};
static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
				     struct platform_device *pdev)
{
	struct pcie_port *pp = &imx6_pcie->pp;
	struct device *dev = pp->dev;
	int ret;
	if (IS_ENABLED(CONFIG_PCI_MSI)) {
		pp->msi_irq = platform_get_irq_byname(pdev, "msi");
		if (pp->msi_irq <= 0) {
			dev_err(dev, "failed to get MSI irq\n");
			return -ENODEV;
		}
		ret = devm_request_irq(dev, pp->msi_irq,
				       imx6_pcie_msi_handler,
				       IRQF_SHARED | IRQF_NO_THREAD,
				       "mx6-pcie-msi", imx6_pcie);
		if (ret) {
			dev_err(dev, "failed to request MSI irq\n");
			return ret;
		}
	}
	pp->root_bus_nr = -1;
	pp->ops = &imx6_pcie_host_ops;
	ret = dw_pcie_host_init(pp);
	if (ret) {
		dev_err(dev, "failed to initialize host\n");
		return ret;
	}
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lucas stach | lucas stach | 74 | 47.13% | 1 | 14.29% | 
| sean cross | sean cross | 57 | 36.31% | 1 | 14.29% | 
| bjorn helgaas | bjorn helgaas | 22 | 14.01% | 2 | 28.57% | 
| grygorii strashko | grygorii strashko | 2 | 1.27% | 1 | 14.29% | 
| fabio estevam | fabio estevam | 1 | 0.64% | 1 | 14.29% | 
| sachin kamat | sachin kamat | 1 | 0.64% | 1 | 14.29% | 
 | Total | 157 | 100.00% | 7 | 100.00% | 
static int __init imx6_pcie_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct imx6_pcie *imx6_pcie;
	struct pcie_port *pp;
	struct resource *dbi_base;
	struct device_node *node = dev->of_node;
	int ret;
	imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL);
	if (!imx6_pcie)
		return -ENOMEM;
	pp = &imx6_pcie->pp;
	pp->dev = dev;
	imx6_pcie->variant =
		(enum imx6_pcie_variants)of_device_get_match_data(dev);
	/* Added for PCI abort handling */
	hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
		"imprecise external abort");
	dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
	if (IS_ERR(pp->dbi_base))
		return PTR_ERR(pp->dbi_base);
	/* Fetch GPIOs */
	imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
	imx6_pcie->gpio_active_high = of_property_read_bool(node,
						"reset-gpio-active-high");
	if (gpio_is_valid(imx6_pcie->reset_gpio)) {
		ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio,
				imx6_pcie->gpio_active_high ?
					GPIOF_OUT_INIT_HIGH :
					GPIOF_OUT_INIT_LOW,
				"PCIe reset");
		if (ret) {
			dev_err(dev, "unable to get reset gpio\n");
			return ret;
		}
	}
	/* Fetch clocks */
	imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy");
	if (IS_ERR(imx6_pcie->pcie_phy)) {
		dev_err(dev, "pcie_phy clock source missing or invalid\n");
		return PTR_ERR(imx6_pcie->pcie_phy);
	}
	imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus");
	if (IS_ERR(imx6_pcie->pcie_bus)) {
		dev_err(dev, "pcie_bus clock source missing or invalid\n");
		return PTR_ERR(imx6_pcie->pcie_bus);
	}
	imx6_pcie->pcie = devm_clk_get(dev, "pcie");
	if (IS_ERR(imx6_pcie->pcie)) {
		dev_err(dev, "pcie clock source missing or invalid\n");
		return PTR_ERR(imx6_pcie->pcie);
	}
	if (imx6_pcie->variant == IMX6SX) {
		imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
							   "pcie_inbound_axi");
		if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
			dev_err(dev,
				"pcie_incbound_axi clock missing or invalid\n");
			return PTR_ERR(imx6_pcie->pcie_inbound_axi);
		}
	}
	/* Grab GPR config register range */
	imx6_pcie->iomuxc_gpr =
		 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
	if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
		dev_err(dev, "unable to find iomuxc registers\n");
		return PTR_ERR(imx6_pcie->iomuxc_gpr);
	}
	/* Grab PCIe PHY Tx Settings */
	if (of_property_read_u32(node, "fsl,tx-deemph-gen1",
				 &imx6_pcie->tx_deemph_gen1))
		imx6_pcie->tx_deemph_gen1 = 0;
	if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db",
				 &imx6_pcie->tx_deemph_gen2_3p5db))
		imx6_pcie->tx_deemph_gen2_3p5db = 0;
	if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db",
				 &imx6_pcie->tx_deemph_gen2_6db))
		imx6_pcie->tx_deemph_gen2_6db = 20;
	if (of_property_read_u32(node, "fsl,tx-swing-full",
				 &imx6_pcie->tx_swing_full))
		imx6_pcie->tx_swing_full = 127;
	if (of_property_read_u32(node, "fsl,tx-swing-low",
				 &imx6_pcie->tx_swing_low))
		imx6_pcie->tx_swing_low = 127;
	/* Limit link speed */
	ret = of_property_read_u32(node, "fsl,max-link-speed",
				   &imx6_pcie->link_gen);
	if (ret)
		imx6_pcie->link_gen = 1;
	ret = imx6_add_pcie_port(imx6_pcie, pdev);
	if (ret < 0)
		return ret;
	platform_set_drvdata(pdev, imx6_pcie);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 280 | 48.61% | 1 | 8.33% | 
| justin waters | justin waters | 109 | 18.92% | 1 | 8.33% | 
| fabio estevam | fabio estevam | 57 | 9.90% | 2 | 16.67% | 
| christoph fritz | christoph fritz | 51 | 8.85% | 1 | 8.33% | 
| tim harvey | tim harvey | 24 | 4.17% | 1 | 8.33% | 
| petr stetiar | petr stetiar | 16 | 2.78% | 1 | 8.33% | 
| bjorn helgaas | bjorn helgaas | 15 | 2.60% | 3 | 25.00% | 
| lucas stach | lucas stach | 15 | 2.60% | 1 | 8.33% | 
| andrey smirnov | andrey smirnov | 9 | 1.56% | 1 | 8.33% | 
 | Total | 576 | 100.00% | 12 | 100.00% | 
static void imx6_pcie_shutdown(struct platform_device *pdev)
{
	struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev);
	/* bring down link, so bootloader gets clean state in case of reboot */
	imx6_pcie_assert_core_reset(imx6_pcie);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lucas stach | lucas stach | 27 | 100.00% | 1 | 100.00% | 
 | Total | 27 | 100.00% | 1 | 100.00% | 
static const struct of_device_id imx6_pcie_of_match[] = {
	{ .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
	{ .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
	{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
	{},
};
static struct platform_driver imx6_pcie_driver = {
	.driver = {
		.name	= "imx6q-pcie",
		.of_match_table = imx6_pcie_of_match,
        },
	.shutdown = imx6_pcie_shutdown,
};
static int __init imx6_pcie_init(void)
{
	return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 18 | 100.00% | 1 | 100.00% | 
 | Total | 18 | 100.00% | 1 | 100.00% | 
device_initcall(imx6_pcie_init);
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| sean cross | sean cross | 1288 | 42.13% | 1 | 2.44% | 
| bjorn helgaas | bjorn helgaas | 411 | 13.44% | 12 | 29.27% | 
| lucas stach | lucas stach | 387 | 12.66% | 6 | 14.63% | 
| marek vasut | marek vasut | 231 | 7.56% | 3 | 7.32% | 
| christoph fritz | christoph fritz | 184 | 6.02% | 1 | 2.44% | 
| andrey smirnov | andrey smirnov | 149 | 4.87% | 2 | 4.88% | 
| justin waters | justin waters | 139 | 4.55% | 1 | 2.44% | 
| troy kisky | troy kisky | 72 | 2.36% | 1 | 2.44% | 
| fabio estevam | fabio estevam | 70 | 2.29% | 5 | 12.20% | 
| tim harvey | tim harvey | 55 | 1.80% | 2 | 4.88% | 
| richard zhu | richard zhu | 38 | 1.24% | 2 | 4.88% | 
| petr stetiar | petr stetiar | 26 | 0.85% | 1 | 2.44% | 
| joao pinto | joao pinto | 3 | 0.10% | 1 | 2.44% | 
| grygorii strashko | grygorii strashko | 2 | 0.07% | 1 | 2.44% | 
| sachin kamat | sachin kamat | 1 | 0.03% | 1 | 2.44% | 
| paul gortmaker | paul gortmaker | 1 | 0.03% | 1 | 2.44% | 
 | Total | 3057 | 100.00% | 41 | 100.00% |