[PATCH 3/4] PCI: Add driver for i.MX6 PCI Express

Sascha Hauer s.hauer at pengutronix.de
Tue Jul 2 18:03:27 EST 2013


On Mon, Jul 01, 2013 at 07:15:46AM +0000, Sean Cross wrote:
> This adds a PCI Express port driver for the on-chip PCI Express port
> present on the i.MX6 SoC.  It is based on the PCI Express driver available
> in the Freescale BSP.
> 
> Signed-off-by: Sean Cross <xobs at kosagi.com>
> ---
>  .../devicetree/bindings/pci/imx6q-pcie.txt         |   20 +
>  arch/arm/mach-imx/Kconfig                          |    1 +
>  drivers/pci/pcie/Kconfig                           |   10 +
>  drivers/pci/pcie/Makefile                          |    2 +
>  drivers/pci/pcie/pcie-imx.c                        | 1049 ++++++++++++++++++++
>  5 files changed, 1082 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/imx6q-pcie.txt
>  create mode 100644 drivers/pci/pcie/pcie-imx.c
> 
> diff --git a/Documentation/devicetree/bindings/pci/imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/imx6q-pcie.txt
> new file mode 100644
> index 0000000..2dc9eae
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/imx6q-pcie.txt
> @@ -0,0 +1,20 @@
> +* Freescale i.MX6Q PCI Express bridge
> +
> +Example (i.MX6Q)
> +	pcie: pcie at 01ffc000 {
> +		compatible = "fsl,imx6q-pcie";
> +		reg = <0x01ffc000 0x4000>,
> +		      <0x01000000 0x100000>,
> +		      <0x01100000 0xe00000>,
> +		      <0x01f00000 0xfc000>;
> +		interrupts = <0 122 0x04>;
> +		clocks = <&clks 186>, <&clks 189>, <&clks 196>,
> +			 <&clks 198>, <&clks 144>;
> +		clock-names = "sata_ref", "pcie_ref_125m", "lvds1_sel",
> +			      "lvds1", "pcie_axi";
> +		power-enable = <&gpio7 12 0>;
> +		pcie-reset = <&gpio3 29 0>;
> +		wake-up = <&gpio3 22 0>;
> +		disable-endpoint = <&gpio2 16 0>;
> +	};
> +
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> index ba44328..cad4e5a 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -811,6 +811,7 @@ config SOC_IMX6Q
>  	select PL310_ERRATA_588369 if CACHE_PL310
>  	select PL310_ERRATA_727915 if CACHE_PL310
>  	select PL310_ERRATA_769419 if CACHE_PL310
> +	select MIGHT_HAVE_PCI
>  	select PM_OPP if PM
>  
>  	help
> diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
> index 569f82f..d1d70db 100644
> --- a/drivers/pci/pcie/Kconfig
> +++ b/drivers/pci/pcie/Kconfig
> @@ -83,3 +83,13 @@ endchoice
>  config PCIE_PME
>  	def_bool y
>  	depends on PCIEPORTBUS && PM_RUNTIME
> +
> +#
> +# Platform driver for i.MX6
> +#
> +config PCIE_IMX
> +        bool "Support for i.MX6"
> +        depends on SOC_IMX6Q
> +        help
> +          Enable support for the 1x PCI Express bus on the Freescale i.MX6
> +        depends on PCIEPORTBUS && PM_RUNTIME
> diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
> index 00c62df..5393d21 100644
> --- a/drivers/pci/pcie/Makefile
> +++ b/drivers/pci/pcie/Makefile
> @@ -14,3 +14,5 @@ obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
>  obj-$(CONFIG_PCIEAER)		+= aer/
>  
>  obj-$(CONFIG_PCIE_PME) += pme.o
> +
> +obj-$(CONFIG_PCIE_IMX)		+= pcie-imx.o
> diff --git a/drivers/pci/pcie/pcie-imx.c b/drivers/pci/pcie/pcie-imx.c
> new file mode 100644
> index 0000000..664679e
> --- /dev/null
> +++ b/drivers/pci/pcie/pcie-imx.c
> @@ -0,0 +1,1049 @@
> +/*
> + * drivers/pci/pcie/pcie-imx.c
> + *
> + * PCIe host controller driver for IMX6 SOCs
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * Code originally taken from Freescale linux-2.6.35 BSP.
> + *
> + * Other bits taken from arch/arm/mach-dove/pcie.c
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/types.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of_device.h>
> +#include <linux/clk-provider.h>
> +#include <linux/regmap.h>
> +#include <linux/rfkill.h>
> +
> +#include <asm/sizes.h>
> +#include <asm/io.h>
> +
> +
> +/* IOMUXC */
> +#define IOMUXC_GPR0                     (0x00)
> +#define IOMUXC_GPR1                     (0x04)
> +#define IOMUXC_GPR2                     (0x08)
> +#define IOMUXC_GPR3                     (0x0C)
> +#define IOMUXC_GPR4                     (0x10)
> +#define IOMUXC_GPR5                     (0x14)
> +#define IOMUXC_GPR6                     (0x18)
> +#define IOMUXC_GPR7                     (0x1C)
> +#define IOMUXC_GPR8                     (0x20)
> +#define IOMUXC_GPR9                     (0x24)
> +#define IOMUXC_GPR10                    (0x28)
> +#define IOMUXC_GPR11                    (0x2C)
> +#define IOMUXC_GPR12                    (0x30)
> +#define IOMUXC_GPR13                    (0x34)

These are already defined in include/linux/mfd/syscon/imx6q-iomuxc-gpr.h

> +
> +
> +/* Register Definitions */
> +#define PRT_LOG_R_BaseAddress 0x700
> +
> +/* Register DEBUG_R0 */
> +/* Debug Register 0 */
> +#define DEBUG_R0 (PRT_LOG_R_BaseAddress + 0x28)
> +#define DEBUG_R0_RegisterSize 32
> +#define DEBUG_R0_RegisterResetValue 0x0
> +#define DEBUG_R0_RegisterResetMask 0xFFFFFFFF
> +/* End of Register Definition for DEBUG_R0 */
> +
> +/* Register DEBUG_R1 */
> +/* Debug Register 1 */
> +#define DEBUG_R1 (PRT_LOG_R_BaseAddress + 0x2c)
> +#define DEBUG_R1_RegisterSize 32
> +#define DEBUG_R1_RegisterResetValue 0x0
> +#define DEBUG_R1_RegisterResetMask 0xFFFFFFFF
> +/* End of Register Definition for DEBUG_R1 */
> +
> +#define ATU_R_BaseAddress 0x900
> +#define PCIE_PL_iATUVR (ATU_R_BaseAddress + 0x0)
> +#define PCIE_PL_iATURC1 (ATU_R_BaseAddress + 0x4)
> +#define PCIE_PL_iATURC2 (ATU_R_BaseAddress + 0x8)
> +#define PCIE_PL_iATURLBA (ATU_R_BaseAddress + 0xC)
> +#define PCIE_PL_iATURUBA (ATU_R_BaseAddress + 0x10)
> +#define PCIE_PL_iATURLA (ATU_R_BaseAddress + 0x14)
> +#define PCIE_PL_iATURLTA (ATU_R_BaseAddress + 0x18)
> +#define PCIE_PL_iATURUTA (ATU_R_BaseAddress + 0x1C)

PleaseGetRidOfThisCamelCase.

> +
> +/* GPR1: iomuxc_gpr1_pcie_ref_clk_en(iomuxc_gpr1[16]) */
> +#define iomuxc_gpr1_pcie_ref_clk_en		(1 << 16)
> +/* GPR1: iomuxc_gpr1_test_powerdown(iomuxc_gpr1_18) */
> +#define iomuxc_gpr1_test_powerdown		(1 << 18)

Defines should use uppercase letters. Besides, we already have these in
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h. If there's something
missing add them there not in your driver.

> +
> +/* GPR12: iomuxc_gpr12_los_level(iomuxc_gpr12[8:4]) */
> +#define iomuxc_gpr12_los_level			(0x1F << 4)
> +/* GPR12: iomuxc_gpr12_app_ltssm_enable(iomuxc_gpr12[10]) */
> +#define iomuxc_gpr12_app_ltssm_enable		(1 << 10)
> +/* GPR12: iomuxc_gpr12_device_type(iomuxc_gpr12[15:12]) */
> +#define iomuxc_gpr12_device_type		(0xF << 12)
> +
> +/* GPR8: iomuxc_gpr8_tx_deemph_gen1(iomuxc_gpr8[5:0]) */
> +#define iomuxc_gpr8_tx_deemph_gen1		(0x3F << 0)
> +/* GPR8: iomuxc_gpr8_tx_deemph_gen2_3p5db(iomuxc_gpr8[11:6]) */
> +#define iomuxc_gpr8_tx_deemph_gen2_3p5db	(0x3F << 6)
> +/* GPR8: iomuxc_gpr8_tx_deemph_gen2_6db(iomuxc_gpr8[17:12]) */
> +#define iomuxc_gpr8_tx_deemph_gen2_6db		(0x3F << 12)
> +/* GPR8: iomuxc_gpr8_tx_swing_full(iomuxc_gpr8[24:18]) */
> +#define iomuxc_gpr8_tx_swing_full		(0x7F << 18)
> +/* GPR8: iomuxc_gpr8_tx_swing_low(iomuxc_gpr8[31:25]) */
> +#define iomuxc_gpr8_tx_swing_low		(0x7F << 25)
> +
> +/* Registers of PHY */
> +/* Register PHY_STS_R */
> +/* PHY Status Register */
> +#define PHY_STS_R (PRT_LOG_R_BaseAddress + 0x110)
> +
> +/* Register PHY_CTRL_R */
> +/* PHY Control Register */
> +#define PHY_CTRL_R (PRT_LOG_R_BaseAddress + 0x114)
> +
> +#define SSP_CR_SUP_DIG_MPLL_OVRD_IN_LO 0x0011
> +/* FIELD: RES_ACK_IN_OVRD [15:15]
> +// FIELD: RES_ACK_IN [14:14]
> +// FIELD: RES_REQ_IN_OVRD [13:13]
> +// FIELD: RES_REQ_IN [12:12]
> +// FIELD: RTUNE_REQ_OVRD [11:11]
> +// FIELD: RTUNE_REQ [10:10]
> +// FIELD: MPLL_MULTIPLIER_OVRD [9:9]
> +// FIELD: MPLL_MULTIPLIER [8:2]
> +// FIELD: MPLL_EN_OVRD [1:1]
> +// FIELD: MPLL_EN [0:0]

If you would use defines instead of comments these would actually be
useful.

> +*/
> +
> +#define SSP_CR_SUP_DIG_ATEOVRD 0x0010
> +/* FIELD: ateovrd_en [2:2]
> +// FIELD: ref_usb2_en [1:1]
> +// FIELD: ref_clkdiv2 [0:0]
> +*/
> +
> +#define SSP_CR_LANE0_DIG_RX_OVRD_IN_LO 0x1005
> +/* FIELD: RX_LOS_EN_OVRD [13:13]
> +// FIELD: RX_LOS_EN [12:12]
> +// FIELD: RX_TERM_EN_OVRD [11:11]
> +// FIELD: RX_TERM_EN [10:10]
> +// FIELD: RX_BIT_SHIFT_OVRD [9:9]
> +// FIELD: RX_BIT_SHIFT [8:8]
> +// FIELD: RX_ALIGN_EN_OVRD [7:7]
> +// FIELD: RX_ALIGN_EN [6:6]
> +// FIELD: RX_DATA_EN_OVRD [5:5]
> +// FIELD: RX_DATA_EN [4:4]
> +// FIELD: RX_PLL_EN_OVRD [3:3]
> +// FIELD: RX_PLL_EN [2:2]
> +// FIELD: RX_INVERT_OVRD [1:1]
> +// FIELD: RX_INVERT [0:0]
> +*/
> +
> +#define SSP_CR_LANE0_DIG_RX_ASIC_OUT 0x100D
> +/* FIELD: LOS [2:2]
> +// FIELD: PLL_STATE [1:1]
> +// FIELD: VALID [0:0]
> +*/
> +
> +/* control bus bit definition */
> +#define PCIE_CR_CTL_DATA_LOC 0
> +#define PCIE_CR_CTL_CAP_ADR_LOC 16
> +#define PCIE_CR_CTL_CAP_DAT_LOC 17
> +#define PCIE_CR_CTL_WR_LOC 18
> +#define PCIE_CR_CTL_RD_LOC 19
> +#define PCIE_CR_STAT_DATA_LOC 0
> +#define PCIE_CR_STAT_ACK_LOC 16
> +
> +/* End of Register Definitions */
> +
> +#define  PCIE_CONF_BUS(b)		(((b) & 0xFF) << 16)
> +#define  PCIE_CONF_DEV(d)		(((d) & 0x1F) << 11)
> +#define  PCIE_CONF_FUNC(f)		(((f) & 0x7) << 8)
> +#define  PCIE_CONF_REG(r)		((r) & ~0x3)
> +
> +
> +/* Taken from PCI specs */
> +enum {
> +	MemRdWr = 0,
> +	MemRdLk = 1,
> +	IORdWr = 2,
> +	CfgRdWr0 = 4,
> +	CfgRdWr1 = 5
> +};
> +
> +
> +struct imx_pcie_port {
> +	struct device		*dev;
> +	u8			index;
> +	u8			root_bus_nr;
> +	int			interrupt;
> +
> +	struct resource		*dbi;
> +	struct resource		*io;
> +	struct resource 	*mem;
> +	struct resource 	*root;
> +
> +	struct regmap		*iomuxc_gpr;
> +
> +	void __iomem		*root_base;
> +	void __iomem		*dbi_base;
> +	void __iomem		*io_base;
> +	void __iomem		*mem_base;
> +	spinlock_t		conf_lock;
> +
> +	char			io_space_name[16];
> +	char			mem_space_name[16];
> +
> +	struct list_head	next;
> +
> +	struct clk		*lvds1_sel;
> +	struct clk		*lvds1;
> +	struct clk		*pcie_ref_125m;
> +	struct clk		*pcie_axi;
> +	struct clk		*sata_ref;
> +
> +        unsigned int		pcie_pwr_en;
> +        unsigned int		pcie_rst;
> +        unsigned int		pcie_wake_up;

Whitespace damage here.

> +
> +	struct rfkill		*rfkill;
> +};
> +
> +static const struct of_device_id pcie_of_match[] = {
> +	{
> +		.compatible	= "fsl,imx6q-pcie",
> +		.data		= NULL,

No need to set .data to NULL.

> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, pcie_of_match);
> +
> +static struct list_head pcie_port_list;
> +static struct hw_pci imx_pcie;
> +
> +static int pcie_phy_cr_read(void __iomem *dbi_base, int addr, int *data);
> +static int pcie_phy_cr_write(void __iomem *dbi_base, int addr, int data);
> +
> +
> +/* IMX PCIE GPR configure routines */
> +static void imx_pcie_clrset(struct imx_pcie_port *pp,
> +			    u32 mask, u32 val, u32 reg)
> +{
> +	u32 tmp;
> +	regmap_read(pp->iomuxc_gpr, reg, &tmp);
> +	tmp &= ~mask;
> +	tmp |= (val & mask);
> +	regmap_write(pp->iomuxc_gpr, reg, tmp);
> +}

This duplicates regmap_update_bits without the locking.

> +
> +static void change_field(int *in, int start, int end, int val)
> +{
> +	int mask;
> +	mask = ((0xFFFFFFFF << start) ^ (0xFFFFFFFF << (end + 1))) & 0xFFFFFFFF;
> +	*in = (*in & ~mask) | (val << start);
> +}
> +
> +
> +static struct imx_pcie_port *controller_to_port(int index)
> +{
> +	struct imx_pcie_port *pp;
> +
> +	if (index >= imx_pcie.nr_controllers) {
> +		pr_err("%d exceeded number of controllers %d\n",
> +			index, imx_pcie.nr_controllers);
> +		return NULL;
> +	}
> +
> +	list_for_each_entry(pp, &pcie_port_list, next) {
> +		if (pp->index == index)
> +			return pp;
> +	}
> +	return NULL;
> +}
> +
> +static struct imx_pcie_port *bus_to_port(int bus)
> +{
> +	int i;
> +	int rbus;
> +	struct imx_pcie_port *pp;
> +
> +	for (i = imx_pcie.nr_controllers - 1 ; i >= 0; i--) {
> +		pp = controller_to_port(i);
> +		rbus = pp->root_bus_nr;
> +		if (rbus != -1 && rbus <= bus)
> +			break;
> +	}
> +
> +	return i >= 0 ? pp : NULL;
> +}

There's certainly something wrong here if you have to iterate over lists
to find your own private data. struct pci_sys_data has a private_data
field exactly for this purpose.

> +
> +static int __init imx_pcie_setup(int nr, struct pci_sys_data *sys)
> +{
> +	struct imx_pcie_port *pp;
> +	int ret;
> +
> +	pp = controller_to_port(nr);
> +	if (!pp) {
> +		pr_err("unable to find port %d\n", nr);
> +		return 0;
> +	}
> +
> +	pp->root_bus_nr = sys->busnr;
> +
> +	/*
> +	 * IORESOURCE_MEM
> +	 */
> +	snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
> +			"PCIe %d MEM", pp->index);
> +
> +	pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
> +	pp->mem->name = pp->mem_space_name;
> +	pp->mem->flags = IORESOURCE_MEM;
> +	ret = request_resource(&iomem_resource, pp->mem);
> +	if (ret)
> +		dev_err(pp->dev, "Request PCIe Memory resource failed\n");
> +	pci_add_resource_offset(&sys->resources, pp->mem, sys->mem_offset);
> +
> +
> +	snprintf(pp->io_space_name, sizeof(pp->io_space_name),
> +		 "PCIe %d I/O", pp->index);
> +	pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0;
> +	pp->io->name = pp->io_space_name;
> +	pp->io->flags = IORESOURCE_IO;
> +
> +	ret = request_resource(&iomem_resource, pp->io);
> +	if (ret)
> +		dev_err(pp->dev, "Request PCIe IO resource failed\n");
> +	pci_add_resource_offset(&sys->resources, pp->io, sys->io_offset);
> +
> +	/*
> +	 * IORESOURCE_IO
> +	 */
> +	ret = pci_ioremap_io(PCIBIOS_MIN_IO, pp->io->start);
> +	if (ret)
> +		dev_err(pp->dev, "Request PCIe IO resource failed\n");
> +
> +	return 1;
> +}
> +
> +static int imx_pcie_link_up(struct platform_device *pdev)
> +{
> +	struct imx_pcie_port *pp = platform_get_drvdata(pdev);
> +	int iterations = 200;
> +	u32 rc, ltssm, rx_valid, temp;
> +
> +	rc = 0;
> +	for (iterations = 200; iterations > 0 && !rc; iterations--) {
> +		/* link is debug bit 36, debug register 1 starts at bit 32 */
> +		rc = readl(pp->dbi_base + DEBUG_R1) & (0x1 << (36 - 32)) ;
> +		usleep_range(2000, 3000);
> +
> +		/* 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
> +		 */

Sorry, what? Do others understand what the problem is? Can we clarify
this?

> +		pcie_phy_cr_read(pp->dbi_base, SSP_CR_LANE0_DIG_RX_ASIC_OUT, &rx_valid);
> +		ltssm = readl(pp->dbi_base + DEBUG_R0) & 0x3F;
> +		if ((ltssm == 0x0D) && ((rx_valid & 0x01) == 0)) {
> +			dev_err(&pdev->dev,
> +				"transition to gen2 is stuck, reset PHY!\n");
> +			pcie_phy_cr_read(pp->dbi_base, SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp);
> +			change_field(&temp, 3, 3, 0x1);
> +			change_field(&temp, 5, 5, 0x1);

I wonder how complicated one can write temp |= (1 << 3) | (1 << 5).
Please get rid of this change_field function.

> +			pcie_phy_cr_write(pp->dbi_base, SSP_CR_LANE0_DIG_RX_OVRD_IN_LO,
> +					0x0028);
> +			usleep_range(2000, 3000);
> +			pcie_phy_cr_read(pp->dbi_base, SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp);
> +			change_field(&temp, 3, 3, 0x0);
> +			change_field(&temp, 5, 5, 0x0);
> +			pcie_phy_cr_write(pp->dbi_base, SSP_CR_LANE0_DIG_RX_OVRD_IN_LO,
> +					0x0000);
> +		}
> +
> +	}
> +
> +	if (!rc) {
> +		if (iterations <= 0) {
> +			dev_err(&pdev->dev,
> +				"link up failed, DEBUG_R0:0x%08x, DEBUG_R1:0x%08x  RX_VALID:0x%x!\n",
> +				readl(pp->dbi_base + DEBUG_R0),
> +				readl(pp->dbi_base + DEBUG_R1),
> +				rx_valid);
> +			return -ETIMEDOUT;
> +		}
> +		return -ENODEV;

I wonder under which circumstances you can reach this -ENODEV.

> +	}
> +
> +	return 0;
> +}
> +
> +static int imx_pcie_regions_setup(struct platform_device *pdev,
> +					struct imx_pcie_port *pp)
> +{
> +	void __iomem *dbi_base = pp->dbi_base;
> +	/*
> +	 * i.MX6 defines 16MB in the AXI address map for PCIe.
> +	 *
> +	 * That address space excepted the pcie registers is
> +	 * split and defined into different regions by iATU,
> +	 * with sizes and offsets as follows:
> +	 *
> +	 * 0x0100_0000 --- 0x010F_FFFF 1MB IORESOURCE_IO
> +	 * 0x0110_0000 --- 0x01EF_FFFF 14MB IORESOURCE_MEM
> +	 * 0x01F0_0000 --- 0x01FF_FFFF 1MB Cfg + Registers
> +	 */
> +
> +	/* CMD reg:I/O space, MEM space, and Bus Master Enable */
> +	writel(readl(dbi_base + PCI_COMMAND)
> +			| PCI_COMMAND_IO
> +			| PCI_COMMAND_MEMORY
> +			| PCI_COMMAND_MASTER,
> +			dbi_base + PCI_COMMAND);
> +
> +	/* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
> +	writel(readl(dbi_base + PCI_CLASS_REVISION)
> +			| (PCI_CLASS_BRIDGE_PCI << 16),
> +			dbi_base + PCI_CLASS_REVISION);
> +
> +	/*
> +	 * region0 outbound used to access target cfg
> +	 */
> +	writel(0, dbi_base + PCIE_PL_iATUVR);
> +	writel(pp->root->start, dbi_base + PCIE_PL_iATURLBA);
> +	writel(pp->dbi->end, dbi_base + PCIE_PL_iATURLA);
> +	writel(0, dbi_base + PCIE_PL_iATURUBA);
> +
> +	writel(0, dbi_base + PCIE_PL_iATURLTA);
> +	writel(0, dbi_base + PCIE_PL_iATURUTA);
> +	writel(CfgRdWr0, dbi_base + PCIE_PL_iATURC1);
> +	writel((1<<31), dbi_base + PCIE_PL_iATURC2);
> +
> +	return 0;
> +}
> +
> +
> +static int imx_pcie_valid_config(struct imx_pcie_port *pp,
> +				struct pci_bus *bus, int devfn)
> +{
> +	if (bus->number >= 2)
> +		return 0;
> +
> +	if (devfn != 0)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +
> +static u32 get_bus_address(struct imx_pcie_port *pp,
> +			   struct pci_bus *bus, u32 devfn, int where)
> +{
> +	u32 va_address;
> +	if (bus->number == 0) {
> +		va_address = (u32)pp->dbi_base + (where & ~0x3);
> +	}
> +	else {

The 'else' is supposed to be on the same line as the '}'

> +		va_address = (u32)pp->root_base +
> +					(PCIE_CONF_BUS(bus->number - 1) +
> +					PCIE_CONF_DEV(PCI_SLOT(devfn)) +
> +					PCIE_CONF_FUNC(PCI_FUNC(devfn)) +
> +					PCIE_CONF_REG(where));
> +	}
> +	return va_address;
> +}
> +
> +static int imx_pcie_read_config(struct pci_bus *bus, u32 devfn, int where,
> +			int size, u32 *val)
> +{
> +	struct imx_pcie_port *pp = bus_to_port(bus->number);
> +	u32 va_address;
> +
> +	if (!pp) {
> +		BUG();
> +		return -EINVAL;
> +	}
> +
> +	if (imx_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
> +		*val = 0xffffffff;
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +	}
> +
> +	va_address = get_bus_address(pp, bus, devfn, where);
> +
> +	*val = readl((u32 *)va_address);
> +
> +	if (size == 1)
> +		*val = (*val >> (8 * (where & 3))) & 0xFF;
> +	else if (size == 2)
> +		*val = (*val >> (8 * (where & 3))) & 0xFFFF;
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int imx_pcie_write_config(struct pci_bus *bus, u32 devfn,
> +			int where, int size, u32 val)
> +{
> +	struct imx_pcie_port *pp = bus_to_port(bus->number);
> +	u32 va_address = 0, mask = 0, tmp = 0;
> +	int ret = PCIBIOS_SUCCESSFUL;
> +
> +	if (!pp) {
> +		BUG();
> +		return -EINVAL;
> +	}
> +
> +	if (imx_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +
> +	va_address = get_bus_address(pp, bus, devfn, where);
> +
> +	if (size == 4) {
> +		writel(val, (u32 *)va_address);
> +		goto exit;
> +	}
> +
> +	if (size == 2)
> +		mask = ~(0xFFFF << ((where & 0x3) * 8));
> +	else if (size == 1)
> +		mask = ~(0xFF << ((where & 0x3) * 8));
> +	else
> +		ret = PCIBIOS_BAD_REGISTER_NUMBER;
> +
> +	tmp = readl((u32 *)va_address) & mask;
> +	tmp |= val << ((where & 0x3) * 8);
> +	writel(tmp, (u32 *)va_address);
> +exit:
> +
> +	return ret;
> +}
> +
> +
> +
> +static struct pci_ops imx_pcie_ops = {
> +	.read = imx_pcie_read_config,
> +	.write = imx_pcie_write_config,
> +};
> +
> +static struct pci_bus __init *
> +imx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
> +{
> +	struct imx_pcie_port *pp = controller_to_port(nr);
> +	if (nr > 1)
> +		return NULL;
> +        pp->root_bus_nr = sys->busnr;
> +
> +        return pci_scan_root_bus(NULL, sys->busnr, &imx_pcie_ops, sys,
> +                                 &sys->resources);

Whitespace damage here.

> +}
> +
> +static int __init imx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
> +{
> +	struct imx_pcie_port *pp = controller_to_port(0);
> +	return pp->interrupt;
> +}
> +
> +static struct hw_pci imx_pci __initdata = {
> +	.nr_controllers	= 1,
> +	.setup		= imx_pcie_setup,
> +	.scan		= imx_pcie_scan_bus,
> +	.map_irq	= imx_pcie_map_irq,
> +};
> +
> +/* PHY CR bus acess routines */
> +static int pcie_phy_cr_ack_polling(void __iomem *dbi_base, int max_iterations, int exp_val)
> +{
> +	u32 temp_rd_data, wait_counter = 0;
> +
> +	do {
> +		temp_rd_data = readl(dbi_base + PHY_STS_R);
> +		temp_rd_data = (temp_rd_data >> PCIE_CR_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 ;
> +}
> +
> +static int pcie_phy_cr_cap_addr(void __iomem *dbi_base, int addr)
> +{
> +	u32 temp_wr_data;
> +
> +	/* write addr */
> +	temp_wr_data = addr << PCIE_CR_CTL_DATA_LOC ;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* capture addr */
> +	temp_wr_data |= (0x1 << PCIE_CR_CTL_CAP_ADR_LOC);
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 1))
> +		return 0;
> +
> +	/* deassert cap addr */
> +	temp_wr_data = addr << PCIE_CR_CTL_DATA_LOC;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack de-assetion */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 0))
> +		return 0 ;
> +
> +	return 1 ;

Remove the whitespace before the semicolons, also elsewhere in this
patch.

> +}
> +
> +static int pcie_phy_cr_read(void __iomem *dbi_base, int addr , int *data)
> +{
> +	u32 temp_rd_data, temp_wr_data;
> +
> +	/*  write addr */
> +	/* cap addr */
> +	if (!pcie_phy_cr_cap_addr(dbi_base, addr))
> +		return 0;
> +
> +	/* assert rd signal */
> +	temp_wr_data = 0x1 << PCIE_CR_CTL_RD_LOC;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 1))
> +		return 0;
> +
> +	/* after got ack return data */
> +	temp_rd_data = readl(dbi_base + PHY_STS_R);
> +	*data = (temp_rd_data & (0xffff << PCIE_CR_STAT_DATA_LOC)) ;
> +
> +	/* deassert rd signal */
> +	temp_wr_data = 0x0;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack de-assetion */

s/assetion/assertion/

This typo is multiple times in this patch.

> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 0))
> +		return 0 ;
> +
> +	return 1 ;
> +
> +}
> +
> +static int pcie_phy_cr_write(void __iomem *dbi_base, int addr, int data)
> +{
> +	u32 temp_wr_data;
> +
> +	/* write addr */
> +	/* cap addr */
> +	if (!pcie_phy_cr_cap_addr(dbi_base, addr))
> +		return 0 ;
> +
> +	temp_wr_data = data << PCIE_CR_CTL_DATA_LOC;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* capture data */
> +	temp_wr_data |= (0x1 << PCIE_CR_CTL_CAP_DAT_LOC);
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 1))
> +		return 0 ;
> +
> +	/* deassert cap data */
> +	temp_wr_data = data << PCIE_CR_CTL_DATA_LOC;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack de-assetion */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 0))
> +		return 0;
> +
> +	/* assert wr signal */
> +	temp_wr_data = 0x1 << PCIE_CR_CTL_WR_LOC;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 1))
> +		return 0;
> +
> +	/* deassert wr signal */
> +	temp_wr_data = data << PCIE_CR_CTL_DATA_LOC;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	/* wait for ack de-assetion */
> +	if (!pcie_phy_cr_ack_polling(dbi_base, 100, 0))
> +		return 0;
> +
> +	temp_wr_data = 0x0 ;
> +	writel(temp_wr_data, dbi_base + PHY_CTRL_R);
> +
> +	return 1;
> +}
> +
> +static int imx_pcie_enable_controller(struct platform_device *pdev)
> +{
> +	struct imx_pcie_port *pp = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	/* Enable PCIE power */
> +	gpio_set_value(pp->pcie_pwr_en, 1);
> +
> +	imx_pcie_clrset(pp, iomuxc_gpr1_test_powerdown, 0 << 18, IOMUXC_GPR1);
> +	imx_pcie_clrset(pp, iomuxc_gpr1_pcie_ref_clk_en, 1 << 16, IOMUXC_GPR1);
> +
> +	/* Enable clocks */
> +	ret = clk_set_parent(pp->lvds1_sel, pp->sata_ref);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to set lvds1 parent: %d\n", ret);
> +		return -EINVAL;
> +	}

This should be done in architecture code I think.

> +
> +	ret = clk_prepare_enable(pp->pcie_ref_125m);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to enable pcie_ref_125m: %d\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	ret = clk_prepare_enable(pp->lvds1);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to enable lvds1: %d\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	ret = clk_prepare_enable(pp->pcie_axi);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to enable pcie_axi: %d\n", ret);
> +		return -EINVAL;
> +	}

This function does not disable the already enabled clocks in the error
path. Please fix.

> +
> +
> +	return 0;
> +}
> +
> +static void card_reset(struct platform_device *pdev)
> +{
> +	struct imx_pcie_port *pp = platform_get_drvdata(pdev);
> +
> +	imx_pcie_clrset(pp, iomuxc_gpr1_test_powerdown, 1 << 18, IOMUXC_GPR1);
> +	imx_pcie_clrset(pp, iomuxc_gpr12_app_ltssm_enable, 1 << 10, IOMUXC_GPR12);
> +	imx_pcie_clrset(pp, iomuxc_gpr1_pcie_ref_clk_en, 0 << 16, IOMUXC_GPR1);
> +
> +	gpio_set_value(pp->pcie_rst, 0);
> +	msleep(100);
> +	gpio_set_value(pp->pcie_rst, 1);
> +}
> +
> +static int __init add_pcie_port(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct imx_pcie_port *pp = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	ret = imx_pcie_link_up(pdev);
> +	if (ret) {
> +		dev_info(dev, "IMX PCIe port: link down!\n");
> +		/* Release the clocks, and disable the power */
> +
> +		clk_disable(pp->pcie_axi);
> +		clk_put(pp->pcie_axi);
> +
> +		clk_disable(pp->lvds1);
> +		clk_put(pp->lvds1);
> +
> +		clk_put(pp->pcie_ref_125m);
> +		clk_put(pp->sata_ref);

You clk_put the clocks in the error path of the probe function. Don't
duplicate it.

> +
> +		imx_pcie_clrset(pp, iomuxc_gpr1_pcie_ref_clk_en, 0 << 16,
> +				IOMUXC_GPR1);
> +
> +		/* Disable PCIE power */
> +		gpio_set_value(pp->pcie_pwr_en, 0);
> +
> +		imx_pcie_clrset(pp, iomuxc_gpr1_test_powerdown, 1 << 18,
> +				IOMUXC_GPR1);
> +
> +		return ret;
> +	}
> +
> +	dev_info(dev, "IMX PCIe port: link up.\n");
> +	pp->index = 0;
> +	pp->root_bus_nr = -1;
> +	spin_lock_init(&pp->conf_lock);
> +	return 0;
> +}
> +
> +
> +static int set_pcie_clock_tunings(struct platform_device *pdev)
> +{
> +	struct imx_pcie_port *pp = platform_get_drvdata(pdev);
> +	/* FIXME the field name should be aligned to RM */
> +	imx_pcie_clrset(pp, iomuxc_gpr12_app_ltssm_enable, 0 << 10, IOMUXC_GPR12);
> +
> +	/* configure constant input signal to the pcie ctrl and phy */
> +	imx_pcie_clrset(pp, iomuxc_gpr12_device_type, PCI_EXP_TYPE_ROOT_PORT << 12,
> +			IOMUXC_GPR12);
> +	imx_pcie_clrset(pp, iomuxc_gpr12_los_level, 9 << 4, IOMUXC_GPR12);
> +
> +	imx_pcie_clrset(pp, iomuxc_gpr8_tx_deemph_gen1, 0 << 0, IOMUXC_GPR8);
> +	imx_pcie_clrset(pp, iomuxc_gpr8_tx_deemph_gen2_3p5db, 0 << 6, IOMUXC_GPR8);
> +	imx_pcie_clrset(pp, iomuxc_gpr8_tx_deemph_gen2_6db, 20 << 12, IOMUXC_GPR8);
> +	imx_pcie_clrset(pp, iomuxc_gpr8_tx_swing_full, 127 << 18, IOMUXC_GPR8);
> +	imx_pcie_clrset(pp, iomuxc_gpr8_tx_swing_low, 127 << 25, IOMUXC_GPR8);
> +	return 0;
> +}
> +
> +
> +static int __init imx_pcie_pltfm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct imx_pcie_port *pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
> +	int ret;
> +
> +	platform_set_drvdata(pdev, pp);
> +	pp->dev = &pdev->dev;
> +
> +        pp->pcie_pwr_en = of_get_named_gpio(pdev->dev.of_node,
> +                                "power-enable", 0);
> +        if (gpio_is_valid(pp->pcie_pwr_en))
> +                devm_gpio_request_one(dev, pp->pcie_pwr_en,
> +                                    GPIOF_OUT_INIT_LOW,
> +                                    "PCIe power enable");
> +
> +        pp->pcie_rst = of_get_named_gpio(pdev->dev.of_node,
> +                                "pcie-reset", 0);
> +        if (gpio_is_valid(pp->pcie_rst))
> +                devm_gpio_request_one(dev, pp->pcie_rst,
> +                                    GPIOF_OUT_INIT_LOW,
> +                                    "PCIe reset");
> +
> +        pp->pcie_wake_up = of_get_named_gpio(pdev->dev.of_node,
> +                                "wake-up", 0);
> +        if (gpio_is_valid(pp->pcie_wake_up))
> +                devm_gpio_request_one(dev, pp->pcie_wake_up,
> +                                    GPIOF_IN,
> +                                    "PCIe wake up");

More whitespace damage above.

> +
> +	pp->dbi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!pp->dbi) {
> +		dev_err(dev, "no mmio space\n");
> +		return -EINVAL;
> +	}
> +
> +	pp->dbi_base = devm_request_and_ioremap(&pdev->dev, pp->dbi);
> +	if (!pp->dbi_base) {
> +		pr_err("unable to remap dbi\n");
> +		return -ENOMEM;
> +	}
> +
> +
> +	pp->io = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!pp->io) {
> +		dev_err(dev, "no mmio space\n");
> +		return -EINVAL;
> +	}
> +
> +	pp->mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	if (!pp->mem) {
> +		dev_err(dev, "no mmio space\n");
> +		return -EINVAL;
> +	}
> +
> +	pp->root = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> +	if (!pp->root) {
> +		dev_err(dev, "no root memory space\n");
> +		return -EINVAL;
> +	}
> +
> +	pp->root_base = devm_request_and_ioremap(&pdev->dev, pp->root);
> +	if (!pp->root_base) {
> +		dev_err(&pdev->dev, "unable to remap root mem\n");
> +		return -ENOMEM;
> +	}
> +
> +
> +	pp->interrupt = platform_get_irq(pdev, 0);
> +
> +
> +        /* Setup clocks */

Whitespace damage here.

> +	pp->lvds1_sel = clk_get(dev, "lvds1_sel");

Use devm_clk_get

> +	if (IS_ERR(pp->lvds1_sel)) {
> +		dev_err(dev,
> +			"lvds1_sel clock missing or invalid\n");
> +		ret = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	pp->lvds1 = clk_get(dev, "lvds1");
> +	if (IS_ERR(pp->lvds1)) {
> +		dev_err(dev,
> +			"lvds1 clock select missing or invalid\n");
> +		ret = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	pp->pcie_ref_125m = clk_get(dev, "pcie_ref_125m");
> +	if (IS_ERR(pp->pcie_ref_125m)) {
> +		dev_err(dev,
> +			"pcie_ref_125m clock source missing or invalid\n");
> +		ret = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	pp->pcie_axi = clk_get(dev, "pcie_axi");
> +	if (IS_ERR(pp->pcie_axi)) {
> +		dev_err(dev, "pcie_axi clock source missing or invalid\n");
> +		ret = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	pp->sata_ref = clk_get(dev, "sata_ref");
> +	if (IS_ERR(pp->sata_ref)) {
> +		dev_err(dev, "sata_ref clock source missing or invalid\n");
> +		ret = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	pp->iomuxc_gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
> +	if (IS_ERR(pp->iomuxc_gpr)) {
> +		dev_err(dev, "unable to find iomuxc registers\n");
> +		ret = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	/* togle the external card's reset */
> +	card_reset(pdev);
> +
> +	/* Enable the pwr, clks and so on */
> +	set_pcie_clock_tunings(pdev);
> +	ret = imx_pcie_enable_controller(pdev);
> +	if (ret)
> +		goto err_out;
> +
> +	usleep_range(3000, 4000);
> +	imx_pcie_regions_setup(pdev, pp);
> +	usleep_range(3000, 4000);

Are these usleeps required?

> +
> +	/* start link up */
> +	imx_pcie_clrset(pp, iomuxc_gpr12_app_ltssm_enable, 1 << 10, IOMUXC_GPR12);
> +
> +	/* add the pcie port */
> +	ret = add_pcie_port(pdev);
> +	if (ret)
> +		goto err_out;
> +
> +	pp->index = imx_pcie.nr_controllers;
> +	imx_pcie.nr_controllers++;
> +	list_add_tail(&pp->next, &pcie_port_list);
> +
> +	pci_common_init(&imx_pci);
> +
> +	return 0;
> +
> +err_out:
> +	if (pp->lvds1_sel)
> +		clk_put(pp->lvds1_sel);
> +	if (pp->lvds1)
> +		clk_put(pp->lvds1);
> +	if (pp->pcie_ref_125m)
> +		clk_put(pp->pcie_ref_125m);
> +	if (pp->pcie_axi)
> +		clk_put(pp->pcie_axi);
> +	if (pp->sata_ref)
> +		clk_put(pp->sata_ref);

You shouldn't call clk_put with error codes.

> +	return ret;
> +}
> +
> +static int __exit imx_pcie_pltfm_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct imx_pcie_port *pp = platform_get_drvdata(pdev);
> +
> +	if (pp->rfkill) {
> +		rfkill_unregister(pp->rfkill);
> +		rfkill_destroy(pp->rfkill);
> +		pp->rfkill = NULL;
> +	}
> +
> +	imx_pcie_clrset(pp, iomuxc_gpr1_pcie_ref_clk_en, 0 << 16, IOMUXC_GPR1);
> +	imx_pcie_clrset(pp, iomuxc_gpr1_test_powerdown, 1 << 18, IOMUXC_GPR1);
> +	imx_pcie_clrset(pp, iomuxc_gpr12_app_ltssm_enable, 1 << 10, IOMUXC_GPR12);
> +
> +	/* Release clocks, and disable power  */
> +	if (pp->pcie_axi) {
> +		clk_disable(pp->pcie_axi);
> +		clk_put(pp->pcie_axi);
> +	}
> +
> +	if (pp->lvds1) {
> +		clk_disable(pp->lvds1);
> +		clk_put(pp->lvds1);
> +	}
> +
> +	if (pp->pcie_ref_125m)
> +		clk_put(pp->pcie_ref_125m);
> +
> +	if (pp->sata_ref)
> +		clk_put(pp->sata_ref);

All these tests are *always* true.

> +
> +	gpio_set_value(pp->pcie_rst, 0);
> +	gpio_set_value(pp->pcie_pwr_en, 0);
> +
> +	dev_err(dev, "disabled everything\n");

Remove this.

> +	msleep(500);

Why?

> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver imx_pcie_pltfm_driver = {
> +	.driver = {
> +		.name		= "imx-pcie",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = pcie_of_match,
> +	},
> +	.probe		= imx_pcie_pltfm_probe,
> +	.remove		= __exit_p(imx_pcie_pltfm_remove),
> +};
> +
> +/*****************************************************************************\
> + *                                                                           *
> + * Driver init/exit                                                          *
> + *                                                                           *
> +\*****************************************************************************/
> +
> +static int __init imx_pcie_drv_init(void)
> +{
> +	INIT_LIST_HEAD(&pcie_port_list);
> +	return platform_driver_register(&imx_pcie_pltfm_driver);
> +}
> +
> +static void __exit imx_pcie_drv_exit(void)
> +{
> +	platform_driver_unregister(&imx_pcie_pltfm_driver);
> +}
> +
> +module_init(imx_pcie_drv_init);
> +module_exit(imx_pcie_drv_exit);
> +
> +MODULE_DESCRIPTION("i.MX PCIE platform driver");
> +MODULE_AUTHOR("Sean Cross <xobs at kosagi.com>");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.7.9.5
> 
> _______________________________________________
> devicetree-discuss mailing list
> devicetree-discuss at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/devicetree-discuss
> 

-- 
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 |


More information about the devicetree-discuss mailing list