From patchwork Fri Oct 1 11:56:05 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh KUMAR X-Patchwork-Id: 223782 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o91C4g6P014143 for ; Fri, 1 Oct 2010 12:04:45 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932214Ab0JAL6d (ORCPT ); Fri, 1 Oct 2010 07:58:33 -0400 Received: from eu1sys200aog108.obsmtp.com ([207.126.144.125]:33617 "EHLO eu1sys200aog108.obsmtp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932147Ab0JAL63 (ORCPT ); Fri, 1 Oct 2010 07:58:29 -0400 Received: from source ([164.129.1.35]) (using TLSv1) by eu1sys200aob108.postini.com ([207.126.147.11]) with SMTP ID DSNKTKXM0f0XsAH3+UJik51uxLn1xajV+Ay+@postini.com; Fri, 01 Oct 2010 11:58:27 UTC Received: from zeta.dmz-eu.st.com (ns2.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id C74FED0; Fri, 1 Oct 2010 11:57:50 +0000 (GMT) Received: from mail2.dlh.st.com (mail2.dlh.st.com [10.199.8.22]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 119E92658; Fri, 1 Oct 2010 11:57:46 +0000 (GMT) Received: from localhost (dlhl0509.dlh.st.com [10.199.7.86]) by mail2.dlh.st.com (MOS 3.8.7a) with ESMTP id CUF00991 (AUTH viresh.kumar@st.com); Fri, 1 Oct 2010 17:27:46 +0530 (IST) From: Viresh KUMAR To: linux-arm-kernel@lists.infradead.org, rtc-linux@googlegroups.com, a.zummo@towertech.it, dbrownell@users.sourceforge.net, linux-usb@vger.kernel.org, linux-input@vger.kernel.org, dmitry.torokhov@gmail.com, linux-mtd@lists.infradead.org, dwmw2@infradead.org, linux-kernel@vger.kernel.org, akpm@linux-foundation.org Cc: Pratyush Anand , shiraz.hashim@st.com, vipin.kumar@st.com, deepak.sikri@st.com, armando.visconti@st.com, vipulkumar.samar@st.com, rajeev-dlh.kumar@st.com, bhupesh.sharma@st.com, Viresh Kumar Subject: [PATCH V2 45/69] ST SPEAr: PCIE gadget suppport Date: Fri, 1 Oct 2010 17:26:05 +0530 Message-Id: X-Mailer: git-send-email 1.7.2.2 In-Reply-To: References: In-Reply-To: References: Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Fri, 01 Oct 2010 12:04:45 +0000 (UTC) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b743312..e19deda 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -344,6 +344,16 @@ config DS1682 This driver can also be built as a module. If so, the module will be called ds1682. +config SPEAR13XX_PCIE_GADGET + bool "PCIE gadget support for SPEAr13XX platform" + depends on ARCH_SPEAR13XX + default n + help + This option enables gadget support for PCIE controller. If + board file defines any controller as PCIE endpoint then a sysfs + entry will be created for that controller. User can use these + sysfs node to configure PCIE EP as per his requirements. + config TI_DAC7512 tristate "Texas Instruments DAC7512" depends on SPI && SYSFS diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 42eab95..9408f23 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -33,5 +33,6 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ obj-$(CONFIG_HMC6352) += hmc6352.o obj-y += eeprom/ obj-y += cb710/ +obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c new file mode 100644 index 0000000..dc63a28 --- /dev/null +++ b/drivers/misc/spear13xx_pcie_gadget.c @@ -0,0 +1,888 @@ +/* + * drivers/misc/spear13xx_pcie_gadget.c + * + * Copyright (C) 2010 ST Microelectronics + * Pratyush Anand + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1) +/* In current implementation address translation is done using IN0 only. + * So IN1 start address and IN0 end address has been kept same +*/ +#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1) +#define IN_IO_SIZE (20 * 1024 * 1024 - 1) +#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1) +#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1) +#define IN_MSG_SIZE (12 * 1024 * 1024 - 1) +/* Keep default BAR size as 4K*/ +/* AORAM would be mapped by default*/ +#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1) + +#define INT_TYPE_NO_INT 0 +#define INT_TYPE_INTX 1 +#define INT_TYPE_MSI 2 +struct spear_pcie_gadget_config { + void __iomem *base; + void __iomem *va_app_base; + void __iomem *va_dbi_base; + char int_type[10]; + u32 requested_msi; + u32 configured_msi; + u32 bar0_size; + u32 bar0_rw_offset; + u32 va_bar0_address; +}; + +static void enable_dbi_access(struct pcie_app_reg *app_reg) +{ + /* Enable DBI access */ + writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID), + &app_reg->slv_armisc); + writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID), + &app_reg->slv_awmisc); + +} + +static void disable_dbi_access(struct pcie_app_reg *app_reg) +{ + /* disable DBI access */ + writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID), + &app_reg->slv_armisc); + writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID), + &app_reg->slv_awmisc); + +} + +static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config, + int where, int size, u32 *val) +{ + struct pcie_app_reg *app_reg + = (struct pcie_app_reg *) config->va_app_base; + u32 va_address; + + /* Enable DBI access */ + enable_dbi_access(app_reg); + + va_address = (u32)config->va_dbi_base + (where & ~0x3); + + *val = readl(va_address); + + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + /* Disable DBI access */ + disable_dbi_access(app_reg); +} + +static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config, + int where, int size, u32 val) +{ + struct pcie_app_reg *app_reg + = (struct pcie_app_reg *) config->va_app_base; + u32 va_address; + + /* Enable DBI access */ + enable_dbi_access(app_reg); + + va_address = (u32)config->va_dbi_base + (where & ~0x3); + + if (size == 4) + writel(val, va_address); + else if (size == 2) + writew(val, va_address + (where & 2)); + else if (size == 1) + writeb(val, va_address + (where & 3)); + + /* Disable DBI access */ + disable_dbi_access(app_reg); +} + +#define PCI_FIND_CAP_TTL 48 + +static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config, + u32 pos, int cap, int *ttl) +{ + u32 id; + + while ((*ttl)--) { + spear_dbi_read_reg(config, pos, 1, &pos); + if (pos < 0x40) + break; + pos &= ~3; + spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id); + if (id == 0xff) + break; + if (id == cap) + return pos; + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + +static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config, + u32 pos, int cap) +{ + int ttl = PCI_FIND_CAP_TTL; + + return pci_find_own_next_cap_ttl(config, pos, cap, &ttl); +} + +static int pci_find_own_cap_start(struct spear_pcie_gadget_config *config, + u8 hdr_type) +{ + u32 status; + + spear_dbi_read_reg(config, PCI_STATUS, 2, &status); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + return PCI_CAPABILITY_LIST; + case PCI_HEADER_TYPE_CARDBUS: + return PCI_CB_CAPABILITY_LIST; + default: + return 0; + } + + return 0; +} + +/** + * Tell if a device supports a given PCI capability. + * Returns the address of the requested capability structure within the + * device's PCI configuration space or 0 in case the device does not + * support it. Possible values for @cap: + * + * %PCI_CAP_ID_PM Power Management + * %PCI_CAP_ID_AGP Accelerated Graphics Port + * %PCI_CAP_ID_VPD Vital Product Data + * %PCI_CAP_ID_SLOTID Slot Identification + * %PCI_CAP_ID_MSI Message Signalled Interrupts + * %PCI_CAP_ID_CHSWP CompactPCI HotSwap + * %PCI_CAP_ID_PCIX PCI-X + * %PCI_CAP_ID_EXP PCI Express + */ +static int pci_find_own_capability(struct spear_pcie_gadget_config *config, + int cap) +{ + u32 pos; + u32 hdr_type; + + spear_dbi_read_reg(config, PCI_HEADER_TYPE, 1, &hdr_type); + + pos = pci_find_own_cap_start(config, hdr_type); + if (pos) + pos = pci_find_own_next_cap(config, pos, cap); + + return pos; +} + +static irqreturn_t spear_pcie_gadget_irq(int irq, void *dev_id) +{ + return 0; +} + +static ssize_t pcie_gadget_show_link(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + + if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID)) + return sprintf(buf, "UP"); + else + return sprintf(buf, "DOWN"); +} + +static ssize_t pcie_gadget_store_link(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + char link[10]; + + if (sscanf(buf, "%s", link) != 1) + return -EINVAL; + + if (!strcmp(link, "UP")) + writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID), + &app_reg->app_ctrl_0); + else + writel(readl(&app_reg->app_ctrl_0) + & ~(1 << APP_LTSSM_ENABLE_ID), + &app_reg->app_ctrl_0); + return count; +} + +static DEVICE_ATTR(link, S_IWUSR | S_IRUGO, pcie_gadget_show_link, + pcie_gadget_store_link); + +static ssize_t pcie_gadget_show_int_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + + return sprintf(buf, "%s", config->int_type); +} + +static ssize_t pcie_gadget_store_int_type(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + char int_type[10]; + u32 cap, vector, vec, flags; + + if (sscanf(buf, "%s", int_type) != 1) + return -EINVAL; + + if (!strcmp(int_type, "INTA")) + spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1); + + else if (!strcmp(int_type, "MSI")) { + vector = config->requested_msi; + vec = 0; + while (vector > 1) { + vector /= 2; + vec++; + } + spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0); + cap = pci_find_own_capability(config, PCI_CAP_ID_MSI); + spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags); + flags &= ~PCI_MSI_FLAGS_QMASK; + flags |= vec << 1; + spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags); + } + + strcpy(config->int_type, int_type); + + return count; +} + +static DEVICE_ATTR(int_type, S_IWUSR | S_IRUGO, pcie_gadget_show_int_type, + pcie_gadget_store_int_type); + +static ssize_t pcie_gadget_show_no_of_msi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + u32 cap, vector, vec, flags; + + if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID)) + != (1 << CFG_MSI_EN_ID)) + vector = 0; + else { + cap = pci_find_own_capability(config, PCI_CAP_ID_MSI); + spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags); + flags &= ~PCI_MSI_FLAGS_QSIZE; + vec = flags >> 4; + vector = 1; + while (vec--) + vector *= 2; + } + config->configured_msi = vector; + + return sprintf(buf, "%u", vector); +} + +static ssize_t pcie_gadget_store_no_of_msi(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &config->requested_msi) != 1) + return -EINVAL; + if (config->requested_msi > 32) + config->requested_msi = 32; + + return count; +} + +static DEVICE_ATTR(no_of_msi, S_IWUSR | S_IRUGO, pcie_gadget_show_no_of_msi, + pcie_gadget_store_no_of_msi); + +static ssize_t pcie_gadget_store_inta(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + int en; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en) + writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID), + &app_reg->app_ctrl_0); + else + writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID), + &app_reg->app_ctrl_0); + + return count; +} + +static DEVICE_ATTR(inta, S_IWUSR, NULL, pcie_gadget_store_inta); + +static ssize_t pcie_gadget_store_send_msi(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + int vector; + u32 ven_msi; + + if (sscanf(buf, "%d", &vector) != 1) + return -EINVAL; + + if (!config->configured_msi) + return -EINVAL; + + if (vector >= config->configured_msi) + return -EINVAL; + + ven_msi = readl(&app_reg->ven_msi_1); + ven_msi &= ~VEN_MSI_FUN_NUM_MASK; + ven_msi |= 0 << VEN_MSI_FUN_NUM_ID; + ven_msi &= ~VEN_MSI_TC_MASK; + ven_msi |= 0 << VEN_MSI_TC_ID; + ven_msi &= ~VEN_MSI_VECTOR_MASK; + ven_msi |= vector << VEN_MSI_VECTOR_ID; + + /*generating interrupt for msi vector*/ + ven_msi |= VEN_MSI_REQ_EN; + writel(ven_msi, &app_reg->ven_msi_1); + /*need to wait till this bit is cleared, it is not cleared + * autometically[Bug RTL] TBD*/ + udelay(1); + ven_msi &= ~VEN_MSI_REQ_EN; + writel(ven_msi, &app_reg->ven_msi_1); + + return count; +} + +static DEVICE_ATTR(send_msi, S_IWUSR, NULL, pcie_gadget_store_send_msi); + +static ssize_t pcie_gadget_show_vendor_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 id; + + spear_dbi_read_reg(config, PCI_VENDOR_ID, 2, &id); + + return sprintf(buf, "%x", id); +} + +static ssize_t pcie_gadget_store_vendor_id(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 id; + + if (sscanf(buf, "%x", &id) != 1) + return -EINVAL; + + spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id); + + return count; +} + +static DEVICE_ATTR(vendor_id, S_IWUSR | S_IRUGO, pcie_gadget_show_vendor_id, + pcie_gadget_store_vendor_id); + +static ssize_t pcie_gadget_show_device_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 id; + + spear_dbi_read_reg(config, PCI_DEVICE_ID, 2, &id); + + return sprintf(buf, "%x", id); +} + +static ssize_t pcie_gadget_store_device_id(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 id; + + if (sscanf(buf, "%x", &id) != 1) + return -EINVAL; + + spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id); + + return count; +} + +static DEVICE_ATTR(device_id, S_IWUSR | S_IRUGO, pcie_gadget_show_device_id, + pcie_gadget_store_device_id); + +static ssize_t pcie_gadget_show_bar0_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + + return sprintf(buf, "%x", config->bar0_size); +} + +static ssize_t pcie_gadget_store_bar0_size(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 size, pos, pos1; + u32 no_of_bit = 0; + + if (sscanf(buf, "%x", &size) != 1) + return -EINVAL; + /* as per PCIE specs, min bar size supported is 128 bytes. But + * our controller supports min as 256*/ + if (size <= 0x100) + size = 0x100; + /* max bar size is 1MB*/ + else if (size >= 0x100000) + size = 0x100000; + else { + pos = 0; + pos1 = 0; + while (pos < 21) { + pos = find_next_bit((unsigned long *)&size, 21, pos); + if (pos != 21) + pos1 = pos + 1; + pos++; + no_of_bit++; + } + if (no_of_bit == 2) + pos1--; + + size = 1 << pos1; + } + config->bar0_size = size; + spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1); + + return count; +} + +static DEVICE_ATTR(bar0_size, S_IWUSR | S_IRUGO, pcie_gadget_show_bar0_size, + pcie_gadget_store_bar0_size); + +static ssize_t pcie_gadget_show_bar0_address(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + + u32 address = readl(&app_reg->pim0_mem_addr_start); + + return sprintf(buf, "%x", address); +} + +static ssize_t pcie_gadget_store_bar0_address(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + u32 address; + + if (sscanf(buf, "%x", &address) != 1) + return -EINVAL; + + address &= ~(config->bar0_size - 1); + if (config->va_bar0_address) + iounmap((void *)config->va_bar0_address); + config->va_bar0_address = (u32)ioremap(address, config->bar0_size); + if (!config->va_bar0_address) + return -ENOMEM; + + writel(address, &app_reg->pim0_mem_addr_start); + + return count; +} + +static DEVICE_ATTR(bar0_address, S_IWUSR | S_IRUGO, + pcie_gadget_show_bar0_address, pcie_gadget_store_bar0_address); + +static ssize_t pcie_gadget_show_bar0_rw_offset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + + return sprintf(buf, "%x", config->bar0_rw_offset); +} + +static ssize_t pcie_gadget_store_bar0_rw_offset(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 offset; + + if (sscanf(buf, "%x", &offset) != 1) + return -EINVAL; + + if (offset % 4) + return -EINVAL; + + config->bar0_rw_offset = offset; + + return count; +} + +static DEVICE_ATTR(bar0_rw_offset, S_IWUSR | S_IRUGO, + pcie_gadget_show_bar0_rw_offset, pcie_gadget_store_bar0_rw_offset); + +static ssize_t pcie_gadget_show_bar0_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 data; + + if (!config->va_bar0_address) + return -ENOMEM; + + data = readl(config->va_bar0_address + config->bar0_rw_offset); + + return sprintf(buf, "%x", data); +} + +static ssize_t pcie_gadget_store_bar0_data(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spear_pcie_gadget_config *config = dev_get_drvdata(dev); + u32 data; + + if (sscanf(buf, "%x", &data) != 1) + return -EINVAL; + + if (!config->va_bar0_address) + return -ENOMEM; + + writel(data, config->va_bar0_address + config->bar0_rw_offset); + + return count; +} + +static DEVICE_ATTR(bar0_data, S_IWUSR | S_IRUGO, + pcie_gadget_show_bar0_data, pcie_gadget_store_bar0_data); + +static ssize_t pcie_gadget_show_help(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char text[] = "\t\tlink read->ltssm status\n \ + link write->arg1 = UP to enable ltsmm DOWN to disable\n \ + int_type read->type of supported interrupt\n \ + int_type write->arg1 = interrupt type to be configured and\n \ + can be INTA, MSI or NO_INT\n \ + (select MSI only when you have programmed no_of_msi)\n \ + no_of_msi read->zero if MSI is not enabled by host\n \ + and positive value is the number of MSI vector granted\n \ + no_of_msi write->arg1 = number of MSI vector needed\n \ + inta write->arg1 = 1 to assert INTA and 0 to de-assert\n \ + send_msi write->arg1 = MSI vector to be send\n \ + vendor_id read->programmed vendor id (hex)\n\ + vendor_id write->arg1 = vendor id(hex) to be programmed\n \ + device_id read->programmed device id(hex)\n \ + device_id write->arg1 = device id(hex) to be programmed\n \ + bar0_size read->size of bar0 in hex\n \ + bar0_size write->arg1= size of bar0 in hex\n \ + (default bar0 size is 1000 (hex) bytes)\n \ + bar0_address read->address of bar0 mapped area in hex\n \ + bar0_address write->arg1 = address of bar0 mapped area in hex\n\ + (default mapping of bar0 is SYSRAM1(E0800000)\n \ + (always program bar size before bar address)\n \ + (kernel might modify bar size and address to align)\n \ + (read back bar size and address after writing to check)\n \ + bar0_rw_offset read->offset of bar0 for which bar0_data \n \ + will return value\n \ + bar0_rw_offset write->arg1 = offset of bar0 for which\n \ + bar0_data will write value\n \ + bar0_data read->data at bar0_rw_offset\n \ + bar0_data write->arg1 = data to be written at\n \ + bar0_rw_offset\n"; + + int size = (sizeof(text) < PAGE_SIZE) ? sizeof(text) : PAGE_SIZE; + + return snprintf(buf, size, "%s", text); +} + +static DEVICE_ATTR(help, S_IRUGO, pcie_gadget_show_help, NULL); + +static struct attribute *pcie_gadget_attributes[] = { + &dev_attr_link.attr, + &dev_attr_int_type.attr, + &dev_attr_no_of_msi.attr, + &dev_attr_inta.attr, + &dev_attr_send_msi.attr, + &dev_attr_vendor_id.attr, + &dev_attr_device_id.attr, + &dev_attr_bar0_size.attr, + &dev_attr_bar0_address.attr, + &dev_attr_bar0_rw_offset.attr, + &dev_attr_bar0_data.attr, + &dev_attr_help.attr, + NULL +}; + +static const struct attribute_group pcie_gadget_attr_group = { + .attrs = pcie_gadget_attributes, +}; + +static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config) +{ + struct pcie_app_reg *app_reg = + (struct pcie_app_reg *)config->va_app_base; + + /*setup registers for outbound translation */ + + writel(config->base, &app_reg->in0_mem_addr_start); + writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE, + &app_reg->in0_mem_addr_limit); + writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start); + writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE, + &app_reg->in1_mem_addr_limit); + writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start); + writel(app_reg->in_io_addr_start + IN_IO_SIZE, + &app_reg->in_io_addr_limit); + writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start); + writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE, + &app_reg->in_cfg0_addr_limit); + writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start); + writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE, + &app_reg->in_cfg1_addr_limit); + writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start); + writel(app_reg->in_msg_addr_start + IN_MSG_SIZE, + &app_reg->in_msg_addr_limit); + + writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start); + writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start); + writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start); + + /*setup registers for inbound translation */ + + /* Keep AORAM mapped at BAR0 as default */ + config->bar0_size = INBOUND_ADDR_MASK + 1; + spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK); + spear_dbi_write_reg(config, PCI_BASE_ADDRESS_0, 4, 0xC); + config->va_bar0_address = (u32)ioremap(SPEAR13XX_SYSRAM1_BASE, + config->bar0_size); + + writel(SPEAR13XX_SYSRAM1_BASE, &app_reg->pim0_mem_addr_start); + writel(0, &app_reg->pim1_mem_addr_start); + writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit); + + writel(0x0, &app_reg->pim_io_addr_start); + writel(0x0, &app_reg->pim_io_addr_start); + writel(0x0, &app_reg->pim_rom_addr_start); + + writel(DEVICE_TYPE_EP | (1 << MISCTRL_EN_ID) + | ((u32)1 << REG_TRANSLATION_ENABLE), + &app_reg->app_ctrl_0); + /* disable all rx interrupts */ + writel(0, &app_reg->int_mask); + + /* Select INTA as default*/ + spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1); +} + +static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev) +{ + struct resource *res0, *res1; + struct spear_pcie_gadget_config *config; + unsigned int status = 0; + int irq; + struct clk *clk; + + /* get resource for application registers*/ + + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res0) { + dev_err(&pdev->dev, "no resource defined\n"); + return -EBUSY; + } + if (!request_mem_region(res0->start, resource_size(res0), + pdev->name)) { + dev_err(&pdev->dev, "pcie gadget region already claimed\n"); + return -EBUSY; + } + /* get resource for dbi registers*/ + + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res1) { + dev_err(&pdev->dev, "no resource defined\n"); + goto err_rel_res0; + } + if (!request_mem_region(res1->start, resource_size(res1), + pdev->name)) { + dev_err(&pdev->dev, "pcie gadget region already claimed\n"); + goto err_rel_res0; + } + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) { + dev_err(&pdev->dev, "out of memory\n"); + status = -ENOMEM; + goto err_rel_res; + } + + config->va_app_base = ioremap(res0->start, resource_size(res0)); + if (!config->va_app_base) { + dev_err(&pdev->dev, "ioremap fail\n"); + status = -ENOMEM; + goto err_kzalloc; + } + + config->base = (void *)res1->start; + + config->va_dbi_base = ioremap(res1->start, resource_size(res1)); + if (!config->va_dbi_base) { + dev_err(&pdev->dev, "ioremap fail\n"); + status = -ENOMEM; + goto err_iounmap_app; + } + + dev_set_drvdata(&pdev->dev, config); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no update irq?\n"); + status = irq; + goto err_iounmap; + } + + status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL); + if (status) { + dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \ + claimed\n", irq); + goto err_get_irq; + } + /* Register sysfs hooks */ + status = sysfs_create_group(&pdev->dev.kobj, &pcie_gadget_attr_group); + if (status) + goto err_irq; + + /* init basic pcie application registers*/ + /* do not enable clock if it is PCIE0.Ideally , all controller should + * have been independent from others with respect to clock. But PCIE1 + * and 2 depends on PCIE0.So PCIE0 clk is provided during board init.*/ + if (pdev->id == 1) { + /* Ideally CFG Clock should have been also enabled here. But + * it is done currently during board init routne*/ + clk = clk_get_sys("pcie1", NULL); + if (!clk) { + pr_err("%s:couldn't get clk for pcie1\n", __func__); + goto err_irq; + } + if (clk_enable(clk)) { + pr_err("%s:couldn't enable clk for pcie1\n", __func__); + goto err_irq; + } + } else if (pdev->id == 2) { + /* Ideally CFG Clock should have been also enabled here. But + * it is done currently during board init routne*/ + clk = clk_get_sys("pcie2", NULL); + if (!clk) { + pr_err("%s:couldn't get clk for pcie2\n", __func__); + goto err_irq; + } + if (clk_enable(clk)) { + pr_err("%s:couldn't enable clk for pcie2\n", __func__); + goto err_irq; + } + } + spear13xx_pcie_device_init(config); + + return 0; +err_irq: + free_irq(irq, NULL); +err_get_irq: + dev_set_drvdata(&pdev->dev, NULL); +err_iounmap: + iounmap(config->va_dbi_base); +err_iounmap_app: + iounmap(config->va_app_base); +err_kzalloc: + kfree(config); +err_rel_res: + release_mem_region(res1->start, resource_size(res1)); +err_rel_res0: + release_mem_region(res0->start, resource_size(res0)); + return status; +} + +static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev) +{ + struct resource *res0, *res1; + struct spear_pcie_gadget_config *config; + int irq; + + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + config = dev_get_drvdata(&pdev->dev); + + free_irq(irq, NULL); + dev_set_drvdata(&pdev->dev, NULL); + iounmap(config->va_dbi_base); + iounmap(config->va_app_base); + kfree(config); + release_mem_region(res1->start, resource_size(res1)); + release_mem_region(res0->start, resource_size(res0)); + sysfs_remove_group(&pdev->dev.kobj, &pcie_gadget_attr_group); + + return 0; +} + +static void spear_pcie_gadget_shutdown(struct platform_device *pdev) +{ +} + +static struct platform_driver spear_pcie_gadget_driver = { + .probe = spear_pcie_gadget_probe, + .remove = spear_pcie_gadget_remove, + .shutdown = spear_pcie_gadget_shutdown, + .driver = { + .name = "pcie-gadget-spear", + .bus = &platform_bus_type + }, +}; + +static int __init spear_pcie_gadget_init(void) +{ + return platform_driver_register(&spear_pcie_gadget_driver); +} +module_init(spear_pcie_gadget_init); + +static void __exit spear_pcie_gadget_exit(void) +{ + platform_driver_unregister(&spear_pcie_gadget_driver); +} +module_exit(spear_pcie_gadget_exit); + +MODULE_ALIAS("pcie-gadget-spear"); +MODULE_AUTHOR("Pratyush Anand"); +MODULE_LICENSE("GPL");