All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Dooks <ben.dooks@codethink.co.uk>
To: Phil Edworthy <phil.edworthy@renesas.com>
Cc: linux-pci@vger.kernel.org, linux-sh@vger.kernel.org,
	Bjorn Helgaas <bhelgaas@google.com>,
	Valentine Barshak <valentine.barshak@cogentembedded.com>,
	Simon Horman <horms@verge.net.au>,
	Magnus Damm <magnus.damm@gmail.com>
Subject: Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver
Date: Wed, 26 Feb 2014 16:34:10 +0000	[thread overview]
Message-ID: <530E1782.3070403@codethink.co.uk> (raw)
In-Reply-To: <1393430893-2466-2-git-send-email-phil.edworthy@renesas.com>

On 26/02/14 16:08, Phil Edworthy wrote:
> This PCIe Host driver currently does not support MSI, so cards
> fall back to INTx interrupts.
>
> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>

> +	bool "Renesas R-Car PCIe controller"
> +	depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST)
> +	help
> +	  Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
> +
>   endmenu
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 13fb333..19946f9 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
>   obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
>   obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
>   obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
> +obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
> diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
> new file mode 100644
> index 0000000..d9a315f
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rcar.c
> @@ -0,0 +1,614 @@
> +/*
> + * PCIe driver for Renesas R-Car SoCs
> + *  Copyright (C) 2013 Renesas Electronics Europe Ltd
> + *
> + * Based on:
> + *  arch/sh/drivers/pci/pcie-sh7786.c
> + *  arch/sh/drivers/pci/ops-sh7786.c
> + *  Copyright (C) 2009 - 2011  Paul Mundt
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2.  This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include "pcie-rcar.h"
> +
> +#define DRV_NAME "rcar-pcie"
> +
> +enum chip_id {
> +	RCAR_GENERIC,
> +	RCAR_H1,
> +};
> +
> +#define RCONF(x)	(PCICONF(0)+(x))
> +#define REXPCAP(x)	(EXPCAP(0)+(x))
> +#define RVCCAP(x)	(VCCAP(0)+(x))
> +
> +#define  PCIE_CONF_BUS(b)	(((b) & 0xff) << 24)
> +#define  PCIE_CONF_DEV(d)	(((d) & 0x1f) << 19)
> +#define  PCIE_CONF_FUNC(f)	(((f) & 0x7) << 16)
> +
> +#define NR_PCI_RESOURCES 4
> +
> +/* Structure representing the PCIe interface */
> +struct rcar_pcie {
> +	struct device		*dev;
> +	void __iomem		*base;
> +	int			irq;
> +	struct clk		*clk;
> +	struct resource		*res[NR_PCI_RESOURCES];
> +	int			haslink;
> +	enum chip_id		chip;
> +	u8			root_bus_nr;
> +};
> +
> +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys)
> +{
> +	return sys->private_data;
> +}
> +
> +static void
> +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg)
> +{
> +	writel(val, pcie->base + reg);
> +}

Do we really need wrappers like these?

> +static unsigned long
> +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg)
> +{
> +	return readl(pcie->base + reg);
> +}
> +
> +enum {
> +	PCI_ACCESS_READ,
> +	PCI_ACCESS_WRITE,
> +};

> +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
> +{
> +	struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata);
> +	return pcie->irq;
> +}
> +
> +static void __init rcar_pcie_enable(struct rcar_pcie *pcie)
> +{
> +	struct hw_pci hw;
> +
> +	memset(&hw, 0, sizeof(hw));
> +
> +	hw.nr_controllers = 1;
> +	hw.private_data   = (void **)&pcie;
> +	hw.setup          = rcar_pcie_setup,
> +	hw.map_irq        = rcar_pcie_map_irq,
> +	hw.ops		  = &rcar_pcie_ops,
> +
> +	pci_common_init(&hw);
> +}
> +
> +static int __init phy_wait_for_ack(struct rcar_pcie *pcie)
> +{
> +	unsigned int timeout = 100;
> +
> +	while (timeout--) {
> +		if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK)
> +			return 0;
> +
> +		udelay(100);
> +	}
> +
> +	dev_err(pcie->dev, "Access to PCIe phy timed out\n");
> +
> +	return -ETIMEDOUT;
> +}

Could this be done with some sort of sleep, instead of having
to keep delaying? How long is the average wait for pci to finish
a write?

> +
> +static void __init phy_write_reg(struct rcar_pcie *pcie,
> +				 unsigned int rate, unsigned int addr,
> +				 unsigned int lane, unsigned int data)
> +{
> +	unsigned long phyaddr;
> +
> +	phyaddr = WRITE_CMD |
> +		((rate & 1) << RATE_POS) |
> +		((lane & 0xf) << LANE_POS) |
> +		((addr & 0xff) << ADR_POS);
> +
> +	/* Set write data */
> +	pci_write_reg(pcie, data, H1_PCIEPHYDOUTR);
> +	pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR);
> +
> +	/* Ignore errors as they will be dealt with if the data link is down */
> +	phy_wait_for_ack(pcie);
> +
> +	/* Clear command */
> +	pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR);
> +	pci_write_reg(pcie, 0, H1_PCIEPHYADRR);
> +
> +	/* Ignore errors as they will be dealt with if the data link is down */
> +	phy_wait_for_ack(pcie);
> +}
> +
>

> +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie)
> +{
> +	unsigned int timeout = 100;
> +
> +	while (timeout--) {
> +		if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE))
> +			return 0;
> +
> +		udelay(100);
> +	}
> +
> +	return -ETIMEDOUT;
> +}

Same comments as for previous delay loop


> +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie)
> +{
> +	/* Initialise R-Car H1 PHY & wait for it to be ready */
> +	if (pcie->chip == RCAR_H1)
> +		if (rcar_pcie_phy_init_rcar_h1(pcie))
> +			return;
> +
> +	/* Begin initialization */
> +	pci_write_reg(pcie, 0, PCIETCTLR);
> +
> +	/* Set mode */
> +	pci_write_reg(pcie, 1, PCIEMSR);
> +
> +	/*
> +	 * For target transfers, setup a single 64-bit 4GB mapping at address
> +	 * 0. The hardware can only map memory that starts on a power of two
> +	 * boundary, and size is power of 2, so best to ignore it.
> +	 */
> +	pci_write_reg(pcie, 0, PCIEPRAR(0));
> +	pci_write_reg(pcie, 0, PCIELAR(0));
> +	pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH |
> +		LAM_64BIT | LAR_ENABLE, PCIELAMR(0));
> +	pci_write_reg(pcie, 0, PCIELAR(1));
> +	pci_write_reg(pcie, 0, PCIEPRAR(1));
> +	pci_write_reg(pcie, 0, PCIELAMR(1));
> +
> +	/*
> +	 * Initial header for port config space is type 1, set the device
> +	 * class to match. Hardware takes care of propagating the IDSETR
> +	 * settings, so there is no need to bother with a quirk.
> +	 */
> +	pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1);
> +
> +	/*
> +	 * Setup Secondary Bus Number & Subordinate Bus Number, even though
> +	 * they aren't used, to avoid bridge being detected as broken.
> +	 */
> +	rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1);
> +	rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1);
> +
> +	/* Initialize default capabilities. */
> +	rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP);
> +	rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS),
> +		PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4);
> +	rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f,
> +		PCI_HEADER_TYPE_BRIDGE);
> +
> +	/* Enable data link layer active state reporting */
> +	rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC);
> +
> +	/* Write out the physical slot number = 0 */
> +	rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0);
> +
> +	/* Set the completion timer timeout to the maximum 50ms. */
> +	rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50);
> +
> +	/* Terminate list of capabilities (Next Capability Offset=0) */
> +	rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0);
> +
> +	/* Enable MAC data scrambling. */
> +	rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0);
> +
> +	/* Finish initialization - establish a PCI Express link */
> +	pci_write_reg(pcie, CFINIT, PCIETCTLR);
> +
> +	/* This will timeout if we don't have a link. */
> +	pcie->haslink = !rcar_pcie_wait_for_dl(pcie);
> +
> +	/* Enable INTx interrupts */
> +	rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0);
> +	rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1);
> +	rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8);
> +
> +	/* Enable slave Bus Mastering */
> +	rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK,
> +		PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
> +		PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST);
> +
> +	wmb();
> +}
> +
> +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie)
> +{
> +	int err = 0;
> +
> +	pcie->clk = clk_get(pcie->dev, NULL);
> +	if (IS_ERR(pcie->clk))
> +		err = PTR_ERR(pcie->clk);
> +	else
> +		clk_enable(pcie->clk);
> +
> +	return err;
> +}

Shouldn't you be using pm_runtime for this?

> +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie)
> +{
> +	clk_put(pcie->clk);
> +}
> +
> +struct rcar_pcie_errs {
> +	int bit;
> +	const char *description;
> +};
> +
> +static int __init rcar_pcie_get_resources(struct platform_device *pdev,
> +	struct rcar_pcie *pcie)
> +{
> +	struct resource *res;
> +	int i;
> +	int err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	i = platform_get_irq(pdev, 0);
> +	if (!res || i < 0) {
> +		dev_err(pcie->dev, "cannot get platform resources\n");
> +		return -ENOENT;
> +	}
> +	pcie->irq = i;
> +
> +	err = rcar_pcie_clocks_get(pcie);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to get clocks: %d\n", err);
> +		return err;
> +	}
> +
> +	pcie->base = devm_request_and_ioremap(&pdev->dev, res);
> +	if (!pcie->base) {
> +		dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err);
> +		err = -ENOMEM;
> +		goto err_map_reg;
> +	}
> +
> +	return 0;
> +
> +err_map_reg:
> +	rcar_pcie_clocks_put(pcie);
> +
> +	return err;
> +}
> +
> +static struct platform_device_id rcar_pcie_id_table[] = {
> +	{ "r8a7779-pcie", RCAR_H1 },
> +	{ "r8a7790-pcie", RCAR_GENERIC },
> +	{ "r8a7791-pcie", RCAR_GENERIC },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table);

Really, are you still going to submit drivers without OF bindings?

NAK!

> +static int __init rcar_pcie_probe(struct platform_device *pdev)
> +{
> +	struct rcar_pcie *pcie;
> +	struct resource *res;
> +	unsigned int data;
> +	int i, err;
> +
> +	pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
> +	if (!pcie)
> +		return -ENOMEM;
> +
> +	pcie->dev = &pdev->dev;
> +	pcie->chip = pdev->id_entry->driver_data;
> +
> +	/* Get resources */
> +	err = rcar_pcie_get_resources(pdev, pcie);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to request resources: %d\n", err);
> +		return err;
> +	}
> +
> +	/* Get the I/O and memory ranges */
> +	for (i = 0; i < NR_PCI_RESOURCES; i++) {
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, i+1);
> +		if (!res) {
> +			dev_err(&pdev->dev, "cannot get MEM%d resources\n", i);
> +			return -ENOENT;
> +		}
> +		pcie->res[i] = res;
> +	}
> +
> +	rcar_pcie_hw_init(pcie);
> +
> +	if (!pcie->haslink) {
> +		dev_info(&pdev->dev, "PCI: PCIe link down\n");
> +		return 0;
> +	}
> +
> +	data = pci_read_reg(pcie, MACSR);
> +	dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
> +
> +	rcar_pcie_enable(pcie);
> +
> +	platform_set_drvdata(pdev, pcie);
> +	return 0;
> +}
> +
> +static struct platform_driver rcar_pcie_driver = {
> +	.probe		= rcar_pcie_probe,
> +	.driver = {
> +		.name = DRV_NAME,
> +		.owner = THIS_MODULE,
> +	},
> +	.id_table	= rcar_pcie_id_table,
> +};
> +
> +module_platform_driver(rcar_pcie_driver);
> +
> +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
> +MODULE_DESCRIPTION("Renesas R-Car PCIe driver");
> +MODULE_LICENSE("GPLv2");
> diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h
> new file mode 100644
> index 0000000..a6b407f
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rcar.h
> @@ -0,0 +1,84 @@
> +/*
> + * PCI Express definitions for Renesas R-Car SoCs
> + */
> +#ifndef __PCI_RCAR_H
> +#define __PCI_RCAR_H
> +
> +#define PCI_DEVICE_ID_RENESAS_RCAR	0x0018

should this go into pci ids header?

> +#define PCIECAR			0x000010
> +#define PCIECCTLR		0x000018
> +#define  CONFIG_SEND_ENABLE	(1 << 31)
> +#define  TYPE0			(0 << 8)
> +#define  TYPE1			(1 << 8)
> +#define PCIECDR			0x000020
> +#define PCIEMSR			0x000028
> +#define PCIEINTXR		0x000400
> +#define PCIEPHYSR		0x0007f0
> +
> +/* Transfer control */
> +#define PCIETCTLR		0x02000
> +#define  CFINIT			1
> +#define PCIETSTR		0x02004
> +#define  DATA_LINK_ACTIVE	1
> +#define PCIEINTR		0x02008
> +#define PCIEINTER		0x0200c
> +#define PCIEERRFR		0x02020
> +#define  UNSUPPORTED_REQUEST	(1 << 4)
> +#define PCIEERRFER		0x02024
> +#define PCIEERRFR2		0x02028
> +#define PCIEPMSR		0x02034
> +#define PCIEPMSCIER		0x02038
> +#define PCIEMSIFR		0x02044
> +
> +/* root port address */
> +#define PCIEPRAR(x)		(0x02080 + ((x) * 0x4))
> +
> +/* local address reg & mask */
> +#define PCIELAR(x)		(0x02200 + ((x) * 0x20))
> +#define PCIELAMR(x)		(0x02208 + ((x) * 0x20))
> +#define  LAM_PMIOLAMnB3		(1 << 3)
> +#define  LAM_PMIOLAMnB2		(1 << 2)
> +#define  LAM_PREFETCH		(1 << 3)
> +#define  LAM_64BIT		(1 << 2)
> +#define  LAR_ENABLE		(1 << 1)
> +
> +/* PCIe address reg & mask */
> +#define PCIEPARL(x)		(0x03400 + ((x) * 0x20))
> +#define PCIEPARH(x)		(0x03404 + ((x) * 0x20))
> +#define PCIEPAMR(x)		(0x03408 + ((x) * 0x20))
> +#define PCIEPTCTLR(x)		(0x0340c + ((x) * 0x20))
> +#define  PAR_ENABLE		(1 << 31)
> +#define  IO_SPACE		(1 << 8)
> +
> +/* Configuration */
> +#define PCICONF(x)		(0x010000 + ((x) * 0x4))
> +#define PMCAP(x)		(0x010040 + ((x) * 0x4))
> +#define MSICAP(x)		(0x010050 + ((x) * 0x4))
> +#define EXPCAP(x)		(0x010070 + ((x) * 0x4))
> +#define VCCAP(x)		(0x010100 + ((x) * 0x4))
> +#define SERNUMCAP(x)		(0x0101b0 + ((x) * 0x4))
> +
> +/* link layer */
> +#define IDSETR0			0x011000
> +#define IDSETR1			0x011004
> +#define SUBIDSETR		0x011024
> +#define DSERSETR0		0x01102c
> +#define DSERSETR1		0x011030
> +#define TLCTLR			0x011048
> +#define MACSR			0x011054
> +#define MACCTLR			0x011058
> +#define  SCRAMBLE_DISABLE	(1 << 27)
> +
> +/* R-Car H1 PHY */
> +#define H1_PCIEPHYCTLR		0x040008
> +#define H1_PCIEPHYADRR		0x04000c
> +#define  WRITE_CMD		(1 << 16)
> +#define  PHY_ACK		(1 << 24)
> +#define  RATE_POS		12
> +#define  LANE_POS		8
> +#define  ADR_POS		0
> +#define H1_PCIEPHYDOUTR		0x040014
> +#define H1_PCIEPHYSR		0x040018
> +
> +#endif /* __PCI_RCAR_H */
>


-- 
Ben Dooks				http://www.codethink.co.uk/
Senior Engineer				Codethink - Providing Genius

WARNING: multiple messages have this Message-ID (diff)
From: Ben Dooks <ben.dooks@codethink.co.uk>
To: Phil Edworthy <phil.edworthy@renesas.com>
Cc: linux-pci@vger.kernel.org, linux-sh@vger.kernel.org,
	Bjorn Helgaas <bhelgaas@google.com>,
	Valentine Barshak <valentine.barshak@cogentembedded.com>,
	Simon Horman <horms@verge.net.au>,
	Magnus Damm <magnus.damm@gmail.com>
Subject: Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver
Date: Wed, 26 Feb 2014 16:34:10 +0000	[thread overview]
Message-ID: <530E1782.3070403@codethink.co.uk> (raw)
In-Reply-To: <1393430893-2466-2-git-send-email-phil.edworthy@renesas.com>

On 26/02/14 16:08, Phil Edworthy wrote:
> This PCIe Host driver currently does not support MSI, so cards
> fall back to INTx interrupts.
>
> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>

> +	bool "Renesas R-Car PCIe controller"
> +	depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST)
> +	help
> +	  Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
> +
>   endmenu
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 13fb333..19946f9 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
>   obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
>   obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
>   obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
> +obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
> diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
> new file mode 100644
> index 0000000..d9a315f
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rcar.c
> @@ -0,0 +1,614 @@
> +/*
> + * PCIe driver for Renesas R-Car SoCs
> + *  Copyright (C) 2013 Renesas Electronics Europe Ltd
> + *
> + * Based on:
> + *  arch/sh/drivers/pci/pcie-sh7786.c
> + *  arch/sh/drivers/pci/ops-sh7786.c
> + *  Copyright (C) 2009 - 2011  Paul Mundt
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2.  This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include "pcie-rcar.h"
> +
> +#define DRV_NAME "rcar-pcie"
> +
> +enum chip_id {
> +	RCAR_GENERIC,
> +	RCAR_H1,
> +};
> +
> +#define RCONF(x)	(PCICONF(0)+(x))
> +#define REXPCAP(x)	(EXPCAP(0)+(x))
> +#define RVCCAP(x)	(VCCAP(0)+(x))
> +
> +#define  PCIE_CONF_BUS(b)	(((b) & 0xff) << 24)
> +#define  PCIE_CONF_DEV(d)	(((d) & 0x1f) << 19)
> +#define  PCIE_CONF_FUNC(f)	(((f) & 0x7) << 16)
> +
> +#define NR_PCI_RESOURCES 4
> +
> +/* Structure representing the PCIe interface */
> +struct rcar_pcie {
> +	struct device		*dev;
> +	void __iomem		*base;
> +	int			irq;
> +	struct clk		*clk;
> +	struct resource		*res[NR_PCI_RESOURCES];
> +	int			haslink;
> +	enum chip_id		chip;
> +	u8			root_bus_nr;
> +};
> +
> +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys)
> +{
> +	return sys->private_data;
> +}
> +
> +static void
> +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg)
> +{
> +	writel(val, pcie->base + reg);
> +}

Do we really need wrappers like these?

> +static unsigned long
> +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg)
> +{
> +	return readl(pcie->base + reg);
> +}
> +
> +enum {
> +	PCI_ACCESS_READ,
> +	PCI_ACCESS_WRITE,
> +};

> +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
> +{
> +	struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata);
> +	return pcie->irq;
> +}
> +
> +static void __init rcar_pcie_enable(struct rcar_pcie *pcie)
> +{
> +	struct hw_pci hw;
> +
> +	memset(&hw, 0, sizeof(hw));
> +
> +	hw.nr_controllers = 1;
> +	hw.private_data   = (void **)&pcie;
> +	hw.setup          = rcar_pcie_setup,
> +	hw.map_irq        = rcar_pcie_map_irq,
> +	hw.ops		  = &rcar_pcie_ops,
> +
> +	pci_common_init(&hw);
> +}
> +
> +static int __init phy_wait_for_ack(struct rcar_pcie *pcie)
> +{
> +	unsigned int timeout = 100;
> +
> +	while (timeout--) {
> +		if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK)
> +			return 0;
> +
> +		udelay(100);
> +	}
> +
> +	dev_err(pcie->dev, "Access to PCIe phy timed out\n");
> +
> +	return -ETIMEDOUT;
> +}

Could this be done with some sort of sleep, instead of having
to keep delaying? How long is the average wait for pci to finish
a write?

> +
> +static void __init phy_write_reg(struct rcar_pcie *pcie,
> +				 unsigned int rate, unsigned int addr,
> +				 unsigned int lane, unsigned int data)
> +{
> +	unsigned long phyaddr;
> +
> +	phyaddr = WRITE_CMD |
> +		((rate & 1) << RATE_POS) |
> +		((lane & 0xf) << LANE_POS) |
> +		((addr & 0xff) << ADR_POS);
> +
> +	/* Set write data */
> +	pci_write_reg(pcie, data, H1_PCIEPHYDOUTR);
> +	pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR);
> +
> +	/* Ignore errors as they will be dealt with if the data link is down */
> +	phy_wait_for_ack(pcie);
> +
> +	/* Clear command */
> +	pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR);
> +	pci_write_reg(pcie, 0, H1_PCIEPHYADRR);
> +
> +	/* Ignore errors as they will be dealt with if the data link is down */
> +	phy_wait_for_ack(pcie);
> +}
> +
>

> +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie)
> +{
> +	unsigned int timeout = 100;
> +
> +	while (timeout--) {
> +		if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE))
> +			return 0;
> +
> +		udelay(100);
> +	}
> +
> +	return -ETIMEDOUT;
> +}

Same comments as for previous delay loop


> +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie)
> +{
> +	/* Initialise R-Car H1 PHY & wait for it to be ready */
> +	if (pcie->chip = RCAR_H1)
> +		if (rcar_pcie_phy_init_rcar_h1(pcie))
> +			return;
> +
> +	/* Begin initialization */
> +	pci_write_reg(pcie, 0, PCIETCTLR);
> +
> +	/* Set mode */
> +	pci_write_reg(pcie, 1, PCIEMSR);
> +
> +	/*
> +	 * For target transfers, setup a single 64-bit 4GB mapping at address
> +	 * 0. The hardware can only map memory that starts on a power of two
> +	 * boundary, and size is power of 2, so best to ignore it.
> +	 */
> +	pci_write_reg(pcie, 0, PCIEPRAR(0));
> +	pci_write_reg(pcie, 0, PCIELAR(0));
> +	pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH |
> +		LAM_64BIT | LAR_ENABLE, PCIELAMR(0));
> +	pci_write_reg(pcie, 0, PCIELAR(1));
> +	pci_write_reg(pcie, 0, PCIEPRAR(1));
> +	pci_write_reg(pcie, 0, PCIELAMR(1));
> +
> +	/*
> +	 * Initial header for port config space is type 1, set the device
> +	 * class to match. Hardware takes care of propagating the IDSETR
> +	 * settings, so there is no need to bother with a quirk.
> +	 */
> +	pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1);
> +
> +	/*
> +	 * Setup Secondary Bus Number & Subordinate Bus Number, even though
> +	 * they aren't used, to avoid bridge being detected as broken.
> +	 */
> +	rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1);
> +	rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1);
> +
> +	/* Initialize default capabilities. */
> +	rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP);
> +	rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS),
> +		PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4);
> +	rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f,
> +		PCI_HEADER_TYPE_BRIDGE);
> +
> +	/* Enable data link layer active state reporting */
> +	rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC);
> +
> +	/* Write out the physical slot number = 0 */
> +	rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0);
> +
> +	/* Set the completion timer timeout to the maximum 50ms. */
> +	rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50);
> +
> +	/* Terminate list of capabilities (Next Capability Offset=0) */
> +	rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0);
> +
> +	/* Enable MAC data scrambling. */
> +	rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0);
> +
> +	/* Finish initialization - establish a PCI Express link */
> +	pci_write_reg(pcie, CFINIT, PCIETCTLR);
> +
> +	/* This will timeout if we don't have a link. */
> +	pcie->haslink = !rcar_pcie_wait_for_dl(pcie);
> +
> +	/* Enable INTx interrupts */
> +	rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0);
> +	rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1);
> +	rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8);
> +
> +	/* Enable slave Bus Mastering */
> +	rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK,
> +		PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
> +		PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST);
> +
> +	wmb();
> +}
> +
> +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie)
> +{
> +	int err = 0;
> +
> +	pcie->clk = clk_get(pcie->dev, NULL);
> +	if (IS_ERR(pcie->clk))
> +		err = PTR_ERR(pcie->clk);
> +	else
> +		clk_enable(pcie->clk);
> +
> +	return err;
> +}

Shouldn't you be using pm_runtime for this?

> +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie)
> +{
> +	clk_put(pcie->clk);
> +}
> +
> +struct rcar_pcie_errs {
> +	int bit;
> +	const char *description;
> +};
> +
> +static int __init rcar_pcie_get_resources(struct platform_device *pdev,
> +	struct rcar_pcie *pcie)
> +{
> +	struct resource *res;
> +	int i;
> +	int err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	i = platform_get_irq(pdev, 0);
> +	if (!res || i < 0) {
> +		dev_err(pcie->dev, "cannot get platform resources\n");
> +		return -ENOENT;
> +	}
> +	pcie->irq = i;
> +
> +	err = rcar_pcie_clocks_get(pcie);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to get clocks: %d\n", err);
> +		return err;
> +	}
> +
> +	pcie->base = devm_request_and_ioremap(&pdev->dev, res);
> +	if (!pcie->base) {
> +		dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err);
> +		err = -ENOMEM;
> +		goto err_map_reg;
> +	}
> +
> +	return 0;
> +
> +err_map_reg:
> +	rcar_pcie_clocks_put(pcie);
> +
> +	return err;
> +}
> +
> +static struct platform_device_id rcar_pcie_id_table[] = {
> +	{ "r8a7779-pcie", RCAR_H1 },
> +	{ "r8a7790-pcie", RCAR_GENERIC },
> +	{ "r8a7791-pcie", RCAR_GENERIC },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table);

Really, are you still going to submit drivers without OF bindings?

NAK!

> +static int __init rcar_pcie_probe(struct platform_device *pdev)
> +{
> +	struct rcar_pcie *pcie;
> +	struct resource *res;
> +	unsigned int data;
> +	int i, err;
> +
> +	pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
> +	if (!pcie)
> +		return -ENOMEM;
> +
> +	pcie->dev = &pdev->dev;
> +	pcie->chip = pdev->id_entry->driver_data;
> +
> +	/* Get resources */
> +	err = rcar_pcie_get_resources(pdev, pcie);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to request resources: %d\n", err);
> +		return err;
> +	}
> +
> +	/* Get the I/O and memory ranges */
> +	for (i = 0; i < NR_PCI_RESOURCES; i++) {
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, i+1);
> +		if (!res) {
> +			dev_err(&pdev->dev, "cannot get MEM%d resources\n", i);
> +			return -ENOENT;
> +		}
> +		pcie->res[i] = res;
> +	}
> +
> +	rcar_pcie_hw_init(pcie);
> +
> +	if (!pcie->haslink) {
> +		dev_info(&pdev->dev, "PCI: PCIe link down\n");
> +		return 0;
> +	}
> +
> +	data = pci_read_reg(pcie, MACSR);
> +	dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
> +
> +	rcar_pcie_enable(pcie);
> +
> +	platform_set_drvdata(pdev, pcie);
> +	return 0;
> +}
> +
> +static struct platform_driver rcar_pcie_driver = {
> +	.probe		= rcar_pcie_probe,
> +	.driver = {
> +		.name = DRV_NAME,
> +		.owner = THIS_MODULE,
> +	},
> +	.id_table	= rcar_pcie_id_table,
> +};
> +
> +module_platform_driver(rcar_pcie_driver);
> +
> +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
> +MODULE_DESCRIPTION("Renesas R-Car PCIe driver");
> +MODULE_LICENSE("GPLv2");
> diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h
> new file mode 100644
> index 0000000..a6b407f
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rcar.h
> @@ -0,0 +1,84 @@
> +/*
> + * PCI Express definitions for Renesas R-Car SoCs
> + */
> +#ifndef __PCI_RCAR_H
> +#define __PCI_RCAR_H
> +
> +#define PCI_DEVICE_ID_RENESAS_RCAR	0x0018

should this go into pci ids header?

> +#define PCIECAR			0x000010
> +#define PCIECCTLR		0x000018
> +#define  CONFIG_SEND_ENABLE	(1 << 31)
> +#define  TYPE0			(0 << 8)
> +#define  TYPE1			(1 << 8)
> +#define PCIECDR			0x000020
> +#define PCIEMSR			0x000028
> +#define PCIEINTXR		0x000400
> +#define PCIEPHYSR		0x0007f0
> +
> +/* Transfer control */
> +#define PCIETCTLR		0x02000
> +#define  CFINIT			1
> +#define PCIETSTR		0x02004
> +#define  DATA_LINK_ACTIVE	1
> +#define PCIEINTR		0x02008
> +#define PCIEINTER		0x0200c
> +#define PCIEERRFR		0x02020
> +#define  UNSUPPORTED_REQUEST	(1 << 4)
> +#define PCIEERRFER		0x02024
> +#define PCIEERRFR2		0x02028
> +#define PCIEPMSR		0x02034
> +#define PCIEPMSCIER		0x02038
> +#define PCIEMSIFR		0x02044
> +
> +/* root port address */
> +#define PCIEPRAR(x)		(0x02080 + ((x) * 0x4))
> +
> +/* local address reg & mask */
> +#define PCIELAR(x)		(0x02200 + ((x) * 0x20))
> +#define PCIELAMR(x)		(0x02208 + ((x) * 0x20))
> +#define  LAM_PMIOLAMnB3		(1 << 3)
> +#define  LAM_PMIOLAMnB2		(1 << 2)
> +#define  LAM_PREFETCH		(1 << 3)
> +#define  LAM_64BIT		(1 << 2)
> +#define  LAR_ENABLE		(1 << 1)
> +
> +/* PCIe address reg & mask */
> +#define PCIEPARL(x)		(0x03400 + ((x) * 0x20))
> +#define PCIEPARH(x)		(0x03404 + ((x) * 0x20))
> +#define PCIEPAMR(x)		(0x03408 + ((x) * 0x20))
> +#define PCIEPTCTLR(x)		(0x0340c + ((x) * 0x20))
> +#define  PAR_ENABLE		(1 << 31)
> +#define  IO_SPACE		(1 << 8)
> +
> +/* Configuration */
> +#define PCICONF(x)		(0x010000 + ((x) * 0x4))
> +#define PMCAP(x)		(0x010040 + ((x) * 0x4))
> +#define MSICAP(x)		(0x010050 + ((x) * 0x4))
> +#define EXPCAP(x)		(0x010070 + ((x) * 0x4))
> +#define VCCAP(x)		(0x010100 + ((x) * 0x4))
> +#define SERNUMCAP(x)		(0x0101b0 + ((x) * 0x4))
> +
> +/* link layer */
> +#define IDSETR0			0x011000
> +#define IDSETR1			0x011004
> +#define SUBIDSETR		0x011024
> +#define DSERSETR0		0x01102c
> +#define DSERSETR1		0x011030
> +#define TLCTLR			0x011048
> +#define MACSR			0x011054
> +#define MACCTLR			0x011058
> +#define  SCRAMBLE_DISABLE	(1 << 27)
> +
> +/* R-Car H1 PHY */
> +#define H1_PCIEPHYCTLR		0x040008
> +#define H1_PCIEPHYADRR		0x04000c
> +#define  WRITE_CMD		(1 << 16)
> +#define  PHY_ACK		(1 << 24)
> +#define  RATE_POS		12
> +#define  LANE_POS		8
> +#define  ADR_POS		0
> +#define H1_PCIEPHYDOUTR		0x040014
> +#define H1_PCIEPHYSR		0x040018
> +
> +#endif /* __PCI_RCAR_H */
>


-- 
Ben Dooks				http://www.codethink.co.uk/
Senior Engineer				Codethink - Providing Genius

  reply	other threads:[~2014-02-26 16:34 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-02-26 16:08 [PATCH 0/5 RFC] R-Car Gen2 PCIe host driver Phil Edworthy
2014-02-26 16:08 ` Phil Edworthy
2014-02-26 16:08 ` [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver Phil Edworthy
2014-02-26 16:08   ` Phil Edworthy
2014-02-26 16:34   ` Ben Dooks [this message]
2014-02-26 16:34     ` Ben Dooks
2014-02-26 17:13     ` Phil.Edworthy
2014-02-26 17:13       ` Phil.Edworthy
2014-02-26 17:19       ` Ben Dooks
2014-02-26 17:19         ` Ben Dooks
2014-02-26 17:34         ` Phil.Edworthy
2014-02-26 17:34           ` Phil.Edworthy
2014-02-26 16:08 ` [PATCH 2/5] ARM: shmobile: r8a7791: Add PCIe clock Phil Edworthy
2014-02-26 16:08 ` [PATCH 3/5] ARM: shmobile: Add PCIe resources to r8a7791 device Phil Edworthy
2014-02-26 16:08 ` [PATCH 4/5] ARM: shmobile: Add PCIe to r8a7791 device Kconfig Phil Edworthy
2014-02-26 16:08 ` [PATCH 5/5] ARM: koelsch: Add PCIe to defconfig Phil Edworthy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=530E1782.3070403@codethink.co.uk \
    --to=ben.dooks@codethink.co.uk \
    --cc=bhelgaas@google.com \
    --cc=horms@verge.net.au \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-sh@vger.kernel.org \
    --cc=magnus.damm@gmail.com \
    --cc=phil.edworthy@renesas.com \
    --cc=valentine.barshak@cogentembedded.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.