From patchwork Tue Oct 29 16:12:51 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Valentine Barshak X-Patchwork-Id: 3108911 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A979A9F431 for ; Tue, 29 Oct 2013 16:13:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id F34492035D for ; Tue, 29 Oct 2013 16:12:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 46168202EB for ; Tue, 29 Oct 2013 16:12:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756925Ab3J2QM4 (ORCPT ); Tue, 29 Oct 2013 12:12:56 -0400 Received: from mail-lb0-f175.google.com ([209.85.217.175]:63488 "EHLO mail-lb0-f175.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753880Ab3J2QM4 (ORCPT ); Tue, 29 Oct 2013 12:12:56 -0400 Received: by mail-lb0-f175.google.com with SMTP id z5so216099lbh.6 for ; Tue, 29 Oct 2013 09:12:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=dzoc8+7zSlnB/5ZRIYm0wtrPOpYIQ91DyS13PiAcB8I=; b=dID6lDBQzXiAMndAVNmF2qA+43Xuk99Y4S/x2HID+UY/77cESS8iq2KX0nY1JGJ2MS gA8xSFu1k6R4AZT7+pczgTLlwzJ28HHbxNlqFNHVLLHZeovEm7lgsftkJ4cEq0uSLAuC Mdf6KTcKzqXJ82l09k8l3XchbkzYr0AXH2tyZ7+XPlSmwMaXsoNMCXxSokoMeGHFWGkC s7zUEOjcngDsSbDpCAwHg7y0yjze8J1gy5QQBx3fr6pF3Bd+3y9YvUBfQv5P2vqM9nf5 mcYYECTaLXp3A00V5DLFhXm3NBfQaI1OH/UUWPaYTIRp6KERXjLuznQM+gxcKnbHC3t9 mQ7Q== X-Gm-Message-State: ALoCoQkhWkV+7F93Q8hR4tXNPff91E4/JvurXRSDNot7zz8tRJZVKQWRlL/7hkWPlWEKTDRpb/a6 X-Received: by 10.152.36.41 with SMTP id n9mr153543laj.21.1383063174262; Tue, 29 Oct 2013 09:12:54 -0700 (PDT) Received: from black.localnet ([93.100.122.208]) by mx.google.com with ESMTPSA id oc1sm16954218lbb.3.2013.10.29.09.12.52 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 29 Oct 2013 09:12:53 -0700 (PDT) From: Valentine Barshak To: linux-sh@vger.kernel.org, linux-pci@vger.kernel.org Cc: Simon Horman , Magnus Damm , Bjorn Helgaas , Kuninori Morimoto , Laurent Pinchart , Guennadi Liakhovetski Subject: [PATCH] pci: Add R-Car Gen2 internal PCI support Date: Tue, 29 Oct 2013 20:12:51 +0400 Message-Id: <1383063171-3144-1-git-send-email-valentine.barshak@cogentembedded.com> X-Mailer: git-send-email 1.8.3.1 Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This adds internal PCI controller driver for R-Car Gen2 SoC. There are three PCI controllers available with only a single EHCI/OHCI device built-in on each PCI bus. This gives us three USB channels. Channel 0 is shared with the USBHS device, while channel 2 is shared with the USBSS. The PCI controllers do not support I/O port space mapping, and it is not needed here. Signed-off-by: Valentine Barshak --- drivers/pci/host/Kconfig | 8 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-rcar-gen2.c | 333 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 drivers/pci/host/pci-rcar-gen2.c diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 3d95048..2e33c2b 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -19,4 +19,12 @@ config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA +config PCI_RCAR_GEN2 + bool "Renesas R-Car Gen2 Internal PCI controller" + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + help + Say Y here if you want internal PCI support on R-Car Gen2 SoC. + There are 3 internal PCI controllers available with a single + built-in EHCI/OHCI host controller present on each one. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index c9a997b..63d9727 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o +obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c new file mode 100644 index 0000000..cbaa5c4 --- /dev/null +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -0,0 +1,333 @@ +/* + * pci-rcar-gen2: internal PCI bus support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* AHB-PCI Bridge PCI communication registers */ +#define RCAR_AHBPCI_PCICOM_OFFSET 0x800 + +#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00) +#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04) +#define RCAR_PCIAHB_PREFETCH0 0x0 +#define RCAR_PCIAHB_PREFETCH4 0x1 +#define RCAR_PCIAHB_PREFETCH8 0x2 +#define RCAR_PCIAHB_PREFETCH16 0x3 + +#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10) +#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14) +#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1) +#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1) +#define RCAR_AHBPCI_WIN1_HOST (1 << 30) +#define RCAR_AHBPCI_WIN1_DEVICE (1 << 31) + +#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) +#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_A (1 << 16) +#define RCAR_PCI_INT_B (1 << 17) +#define RCAR_PCI_INT_PME (1 << 19) + +#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) +#define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) +#define RCAR_AHB_BUS_MMODE_BYTE_BURST (1 << 1) +#define RCAR_AHB_BUS_MMODE_WR_INCR (1 << 2) +#define RCAR_AHB_BUS_MMODE_HBUS_REQ (1 << 7) +#define RCAR_AHB_BUS_SMODE_READYCTR (1 << 17) +#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \ + RCAR_AHB_BUS_MMODE_BYTE_BURST | \ + RCAR_AHB_BUS_MMODE_WR_INCR | \ + RCAR_AHB_BUS_MMODE_HBUS_REQ | \ + RCAR_AHB_BUS_SMODE_READYCTR) + +#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34) +#define RCAR_USBCTR_USBH_RST (1 << 0) +#define RCAR_USBCTR_PCICLK_MASK (1 << 1) +#define RCAR_USBCTR_PLL_RST (1 << 2) +#define RCAR_USBCTR_DIRPD (1 << 8) +#define RCAR_USBCTR_PCIAHB_WIN2_EN (1 << 9) +#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10) + +#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40) +#define RCAR_PCI_ARBITER_PCIREQ0 (1 << 0) +#define RCAR_PCI_ARBITER_PCIREQ1 (1 << 1) +#define RCAR_PCI_ARBITER_PCIBP_MODE (1 << 12) + +#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) + +/* Number of internal PCI controllers */ +#define RCAR_PCI_NR_CONTROLLERS 3 + +struct rcar_pci_priv { + void __iomem *reg; + struct resource io_res; + struct resource mem_res; + struct resource *cfg_res; + int irq; +}; + +/* PCI configuration space operations */ +static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + int slot, val; + + if (sys->busnr != bus->number || PCI_FUNC(devfn)) + return NULL; + + /* Only one EHCI/OHCI device built-in */ + slot = PCI_SLOT(devfn); + if (slot > 2) + return NULL; + + val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : + RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; + + iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG); + return priv->reg + (slot >> 1) * 0x100 + where; +} + +static int rcar_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where); + + if (!reg) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *val = ioread8(reg); + break; + case 2: + *val = ioread16(reg); + break; + default: + *val = ioread32(reg); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where); + + if (!reg) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + iowrite8(val, reg); + break; + case 2: + iowrite16(val, reg); + break; + default: + iowrite32(val, reg); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +/* PCI interrupt mapping */ +static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct pci_sys_data *sys = dev->bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + + return priv->irq; +} + +/* PCI host controller setup */ +static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) +{ + struct rcar_pci_priv *priv = sys->private_data; + void __iomem *reg = priv->reg; + u32 val; + + val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); + pr_info("PCI: bus%u revision %x\n", sys->busnr, val); + + /* Disable Direct Power Down State and assert reset */ + val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; + val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST; + iowrite32(val, reg + RCAR_USBCTR_REG); + udelay(4); + + /* De-assert reset and set PCIAHB window1 size to 1GB */ + val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | + RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); + iowrite32(val | RCAR_USBCTR_PCIAHB_WIN1_1G, reg + RCAR_USBCTR_REG); + + /* Configure AHB master and slave modes */ + iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); + + /* Configure PCI arbiter */ + val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG); + val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 | + RCAR_PCI_ARBITER_PCIBP_MODE; + iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); + + /* PCI-AHB mapping: 0x40000000-0x80000000 */ + iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16, + reg + RCAR_PCIAHB_WIN1_CTR_REG); + + /* AHB-PCI mapping: OHCI/EHCI registers */ + val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM; + iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG); + + /* Enable AHB-PCI bridge PCI configuration access */ + iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG, + reg + RCAR_AHBPCI_WIN1_CTR_REG); + /* Set PCI-AHB Window1 address */ + iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH, + reg + PCI_BASE_ADDRESS_1); + /* Set AHB-PCI bridge PCI communication area address */ + val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET; + iowrite32(val, reg + PCI_BASE_ADDRESS_0); + + val = ioread32(reg + PCI_COMMAND); + val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + iowrite32(val, reg + PCI_COMMAND); + + /* Enable PCI interrupts */ + iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, + reg + RCAR_PCI_INT_ENABLE_REG); + + /* Add PCI resources */ + pci_add_resource(&sys->resources, &priv->io_res); + pci_add_resource(&sys->resources, &priv->mem_res); + + return 1; +} + +static struct pci_ops rcar_pci_ops = { + .read = rcar_pci_read_config, + .write = rcar_pci_write_config, +}; + +static struct hw_pci rcar_hw_pci __initdata = { + .map_irq = rcar_pci_map_irq, + .ops = &rcar_pci_ops, + .setup = rcar_pci_setup, +}; + +static int rcar_pci_count __initdata; + +static int __init rcar_pci_add_controller(struct rcar_pci_priv *priv) +{ + void **private_data; + int count; + + if (rcar_hw_pci.nr_controllers < rcar_pci_count) + goto add_priv; + + /* (Re)allocate private data pointer array if needed */ + count = rcar_pci_count + RCAR_PCI_NR_CONTROLLERS; + private_data = kzalloc(count * sizeof(void *), GFP_KERNEL); + if (!private_data) + return -ENOMEM; + + rcar_pci_count = count; + if (rcar_hw_pci.private_data) { + memcpy(private_data, rcar_hw_pci.private_data, + rcar_hw_pci.nr_controllers * sizeof(void *)); + kfree(rcar_hw_pci.private_data); + } + + rcar_hw_pci.private_data = private_data; + +add_priv: + /* Add private data pointer to the array */ + rcar_hw_pci.private_data[rcar_hw_pci.nr_controllers++] = priv; + return 0; +} + +static int __init rcar_pci_probe(struct platform_device *pdev) +{ + struct resource *cfg_res, *mem_res; + struct rcar_pci_priv *priv; + void __iomem *reg; + + cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, cfg_res); + if (!reg) + return -ENODEV; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!mem_res || !mem_res->start) + return -ENODEV; + + priv = devm_kzalloc(&pdev->dev, + sizeof(struct rcar_pci_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mem_res = *mem_res; + /* + * The controller does not support/use port I/O, + * so setup a dummy port I/O region here. + */ + priv->io_res.start = priv->mem_res.start; + priv->io_res.end = priv->mem_res.end; + priv->io_res.flags = IORESOURCE_IO; + + priv->cfg_res = cfg_res; + + priv->irq = platform_get_irq(pdev, 0); + priv->reg = reg; + + return rcar_pci_add_controller(priv); +} + +static struct platform_driver rcar_pci_driver = { + .driver = { + .name = "pci-rcar-gen2", + }, +}; + +static int __init rcar_pci_init(void) +{ + int retval; + + retval = platform_driver_probe(&rcar_pci_driver, rcar_pci_probe); + if (!retval) + pci_common_init(&rcar_hw_pci); + + /* Private data pointer array is not needed any more */ + kfree(rcar_hw_pci.private_data); + rcar_hw_pci.private_data = NULL; + + return retval; +} + +subsys_initcall(rcar_pci_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen2 internal PCI"); +MODULE_AUTHOR("Valentine Barshak ");