aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/imx6/files-3.10/arch
diff options
context:
space:
mode:
authorLuka Perkov <luka@openwrt.org>2013-07-15 23:18:39 +0000
committerLuka Perkov <luka@openwrt.org>2013-07-15 23:18:39 +0000
commitaf6eb6cc8cc64ff4029cf583b2f250ba16faabac (patch)
treeff6d95beb2790ef6e273cbd08a3ab6430c5ea04b /target/linux/imx6/files-3.10/arch
parent6ef9d30da76819531486bcc2da6488a54c260a34 (diff)
downloadupstream-af6eb6cc8cc64ff4029cf583b2f250ba16faabac.tar.gz
upstream-af6eb6cc8cc64ff4029cf583b2f250ba16faabac.tar.bz2
upstream-af6eb6cc8cc64ff4029cf583b2f250ba16faabac.zip
imx6: add support for gw5400-a
Signed-off-by: Tim Harvey <tharvey@gateworks.com> SVN-Revision: 37363
Diffstat (limited to 'target/linux/imx6/files-3.10/arch')
-rw-r--r--target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-gw5400-a.dts429
-rw-r--r--target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-ventana.dtsi51
-rw-r--r--target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.c156
-rw-r--r--target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.h29
-rw-r--r--target/linux/imx6/files-3.10/arch/arm/mach-imx/pcie.c1037
5 files changed, 1702 insertions, 0 deletions
diff --git a/target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-gw5400-a.dts b/target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-gw5400-a.dts
new file mode 100644
index 0000000000..d79d272b64
--- /dev/null
+++ b/target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-gw5400-a.dts
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q-ventana.dtsi"
+
+/ {
+ model = "Gateworks Ventana GW5400-A";
+ compatible = "gw,imx6q-gw5400-a", "gw,ventana", "fsl,imx6q";
+
+ aliases {
+ ethernet0 = &fec;
+ ethernet1 = &eth1;
+ sky2 = &eth1;
+ };
+
+ /* SDRAM addressing */
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttymxc1,115200";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led0: user0 {
+ label = "user0";
+ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG# */
+ linux,default-trigger = "heartbeat";
+ };
+
+ led1: user1 {
+ label = "user1";
+ gpios = <&gpio4 10 0>; /* 106 -> MX6_PANLEDR# */
+ };
+
+ led2: user2 {
+ label = "user2";
+ gpios = <&gpio4 15 0>; /* 111 -> MX6_LOCLEDR# */
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+
+ reg_2p5v: 2p5v {
+ compatible = "regulator-fixed";
+ regulator-name = "2P5V";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: 3p3v {
+ compatible = "regulator-fixed";
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: usb_otg_vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-sabrelite-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-sabrelite-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <4>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ hog {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ /* USB OTG Power Enable */
+ MX6Q_PAD_EIM_D22__GPIO3_IO22 0x80000000
+
+ /* 3:19 SPINOR_CS0# */
+ MX6Q_PAD_EIM_D19__GPIO3_IO19 0x80000000
+
+ /* 1:09 MX6_DIO0 (could also be PWM1_PWM0) */
+ MX6Q_PAD_GPIO_9__GPIO1_IO09 0x80000000
+ /* 1:19 MX6_DIO1 (could also be PWM2_PWM0) */
+ MX6Q_PAD_SD1_DAT2__GPIO1_IO19 0x80000000
+ /* 2:09 MX6_DIO2 (could also be PWM3_PWM0) */
+ MX6Q_PAD_SD4_DAT1__GPIO2_IO09 0x80000000
+ /* 2:10 MX6_DIO3 (could also be PWM3_PWM0) */
+ MX6Q_PAD_SD4_DAT2__GPIO2_IO10 0x80000000
+
+ /* 1:16 USBHUB_RST# */
+ MX6Q_PAD_SD1_DAT0__GPIO1_IO16 0x80000000
+
+ /* PCIE IRQ */
+ MX6Q_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000
+ /* PCIE RST */
+ MX6Q_PAD_ENET_TXD1__GPIO1_IO29 0x08000000
+
+ /* 1:12 MIPI_DIO */
+ MX6Q_PAD_SD1_DAT3__GPIO1_IO21 0x80000000
+
+ /* AUD4_MCK */
+ MX6Q_PAD_GPIO_0__CCM_CLKO1 0x80000000
+ >;
+ };
+ };
+
+#if 0
+ /* ipu1: IPU1_CSI0: HDMI reciver (Digital Video In) */
+ ipu1 {
+ pinctrl_ipu1_1: ipu1grp-5 {
+ fsl,pins = <
+ MX6Q_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC
+ MX6Q_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN
+ MX6Q_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK
+ MX6Q_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC
+ MX6Q_PAD_CSI0_DAT4__IPU1_CSI0_DATA04
+ MX6Q_PAD_CSI0_DAT5__IPU1_CSI0_DATA05
+ MX6Q_PAD_CSI0_DAT6__IPU1_CSI0_DATA06
+ MX6Q_PAD_CSI0_DAT7__IPU1_CSI0_DATA07
+ MX6Q_PAD_CSI0_DAT8__IPU1_CSI0_DATA08
+ MX6Q_PAD_CSI0_DAT9__IPU1_CSI0_DATA09
+ MX6Q_PAD_CSI0_DAT10__IPU1_CSI0_DATA10
+ MX6Q_PAD_CSI0_DAT11__IPU1_CSI0_DATA11
+ MX6Q_PAD_CSI0_DAT12__IPU1_CSI0_DATA12
+ MX6Q_PAD_CSI0_DAT13__IPU1_CSI0_DATA13
+ MX6Q_PAD_CSI0_DAT14__IPU1_CSI0_DATA14
+ MX6Q_PAD_CSI0_DAT15__IPU1_CSI0_DATA15
+ MX6Q_PAD_CSI0_DAT16__IPU1_CSI0_DATA16
+ MX6Q_PAD_CSI0_DAT17__IPU1_CSI0_DATA17
+ MX6Q_PAD_CSI0_DAT18__IPU1_CSI0_DATA18
+ MX6Q_PAD_CSI0_DAT19__IPU1_CSI0_DATA19
+ >;
+ };
+ };
+
+ /* ipu2: IPU1_CSI1: Analog Video Decoder (Analog Video In) */
+ /* IPU2_CSI1: Analog Video Decoder (Analog Video In) */
+ ipu2 {
+ pinctrl_ipu2_1: ipu2grp-1 {
+ fsl,pins = <
+ MX6Q_PAD_EIM_A17__IPU2_CSI1_DATA12
+ MX6Q_PAD_EIM_D27__IPU2_CSI1_DATA13
+ MX6Q_PAD_EIM_D26__IPU2_CSI1_DATA14
+ MX6Q_PAD_EIM_D20__IPU2_CSI1_DATA15
+ MX6Q_PAD_EIM_D19__IPU2_CSI1_DATA16
+ MX6Q_PAD_EIM_D18__IPU2_CSI1_DATA17
+ MX6Q_PAD_EIM_D16__IPU2_CSI1_DATA18
+ MX6Q_PAD_EIM_EB2__IPU2_CSI1_DATA19
+
+ MX6Q_PAD_EIM_D29__IPU2_CSI1_VSYNC
+ MX6Q_PAD_EIM_EB3__IPU2_CSI1_HSYNC
+// not sure why this causes kernel to crash in early init
+// MX6Q_PAD_EIM_A16__IPU2_CSI1_PIXCLK
+ >;
+ };
+ };
+
+ /* ipu3: IPU2_DISP0: Analog Video Encoder (Analog Video Out) */
+ ipu3 {
+ pinctrl_ipu3_1: ipu3grp-5 {
+ fsl,pins = <
+ MX6Q_PAD_DISP0_DAT0__IPU2_DISP0_DATA00
+ MX6Q_PAD_DISP0_DAT1__IPU2_DISP0_DATA01
+ MX6Q_PAD_DISP0_DAT2__IPU2_DISP0_DATA02
+ MX6Q_PAD_DISP0_DAT3__IPU2_DISP0_DATA03
+ MX6Q_PAD_DISP0_DAT4__IPU2_DISP0_DATA04
+ MX6Q_PAD_DISP0_DAT5__IPU2_DISP0_DATA05
+ MX6Q_PAD_DISP0_DAT6__IPU2_DISP0_DATA06
+ MX6Q_PAD_DISP0_DAT7__IPU2_DISP0_DATA07
+ MX6Q_PAD_DISP0_DAT8__IPU2_DISP0_DATA08
+ MX6Q_PAD_DISP0_DAT9__IPU2_DISP0_DATA09
+ MX6Q_PAD_DISP0_DAT10__IPU2_DISP0_DATA10
+ MX6Q_PAD_DISP0_DAT11__IPU2_DISP0_DATA11
+ MX6Q_PAD_DISP0_DAT12__IPU2_DISP0_DATA12
+ MX6Q_PAD_DISP0_DAT13__IPU2_DISP0_DATA13
+ MX6Q_PAD_DISP0_DAT14__IPU2_DISP0_DATA14
+ MX6Q_PAD_DISP0_DAT15__IPU2_DISP0_DATA15
+ >;
+ };
+ };
+#endif
+};
+
+&ecspi1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi1_1>;
+ status = "okay";
+
+ flash: m25p80@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "sst,w25q256";
+ spi-max-frequency = <30000000>;
+ reg = <0>;
+ };
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1_2>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2_2>;
+ status = "okay";
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart3_1>;
+ status = "okay";
+};
+
+&uart5 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart5_1>;
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&ssi2 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&can1 {
+ reg = <0x02090000 0x4000>;
+ interrupts = <0 110 0x04>;
+ //clock-frequency
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
+
+&pcie {
+ rst-gpios = <&gpio1 29 0>; /* PCIESWT_RST# */
+ clken-gpios = <&gpio1 20 0>; /* not used */
+ status = "okay";
+
+ eth1: sky2@8 { /* MAC/PHY on bus 8 */
+ compatible = "marvell,sky2";
+ /* Filled in by U-Boot */
+ mac-address = [ 00 00 00 00 00 00 ];
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet_1>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 30 0>;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3_2>;
+ cd-gpios = <&gpio7 0 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux_3>;
+ status = "okay";
+};
+
+&i2c1 {
+ status = "okay";
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1_1>;
+
+ eeprom: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom1: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom2: eeprom@51 {
+ compatible = "atmel,24c02";
+ reg = <0x51>;
+ pagesize = <16>;
+ };
+
+ eeprom3: eeprom@52 {
+ compatible = "atmel,24c02";
+ reg = <0x52>;
+ pagesize = <16>;
+ };
+
+ eeprom4: eeprom@53 {
+ compatible = "atmel,24c02";
+ reg = <0x53>;
+ pagesize = <16>;
+ };
+
+ rtc: ds1672@68 {
+ compatible = "dallas,ds1672";
+ reg = <0x68>;
+ };
+
+ gpio: pca9555@23 {
+ compatible = "nxp,pca9555";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ hwmon: gsc@29 {
+ compatible = "gw,gsp";
+ reg = <0x29>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2_2>;
+
+ pmic: pfuze@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x0a>;
+ };
+
+ pciswitch: pex8609@3f {
+ compatible = "plx,pex8609";
+ reg = <0x3f>;
+ };
+
+ pciclkgen: si52147@6b {
+ compatible = "sil,si52147";
+ reg = <0x6b>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3_2>;
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 169>;
+ VDDA-supply = <&reg_2p5v>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+
+ accelerometer: mma8450@1c {
+ compatible = "fsl,mma8450";
+ reg = <0x1c>;
+ };
+
+ videoout: adv7393@2a {
+ compatible = "adi,adv7393";
+ reg = <0x2a>;
+ };
+
+ videoin: adv7180@20 {
+ compatible = "adi,adv7180";
+ reg = <0x20>;
+ };
+
+ hdmiin: adv7611@4c {
+ compatible = "adi,adv7611";
+ reg = <0x4c>;
+ };
+
+ touchscreen: egalax_ts@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ wakeup-gpios = <&gpio1 12 0>;
+ };
+};
+
+&ldb {
+ status = "okay";
+ lvds-channel@0 {
+ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
+ };
+};
diff --git a/target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-ventana.dtsi b/target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-ventana.dtsi
new file mode 100644
index 0000000000..7cebdba1ea
--- /dev/null
+++ b/target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-ventana.dtsi
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "imx6q.dtsi"
+
+/ {
+ model = "Gateworks Ventana";
+ system-serial = "";
+
+ // these are used by bootloader for disabling nodes
+ aliases {
+ // ethernet0 = &eth0;
+ // ethernet1 = &eth1;
+ ssi0 = &ssi1;
+ ssi1 = &ssi2;
+ ipu0 = &ipu1;
+ ipu1 = &ipu2;
+ // mipi_csi = &mipi_csi;
+ // mipi_dsi = &mipi_dsi;
+ usdhc0 = &usdhc1;
+ usdhc1 = &usdhc2;
+ usdhc2 = &usdhc3;
+ usdhc3 = &usdhc4;
+ i2c0 = &i2c1;
+ i2c1 = &i2c2;
+ i2c2 = &i2c3;
+ usb0 = &usbh3;
+ usb1 = &usbotg;
+ spi0 = &ecspi1;
+ spi1 = &ecspi2;
+ spi2 = &ecspi3;
+ spi3 = &ecspi4;
+ spi4 = &ecspi5;
+ pwm0 = &pwm1;
+ pwm1 = &pwm2;
+ pwm2 = &pwm3;
+ pwm3 = &pwm4;
+ can0 = &can1;
+ led0 = &led0;
+ led1 = &led1;
+ led2 = &led2;
+ };
+};
diff --git a/target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.c b/target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.c
new file mode 100644
index 0000000000..8850eaf6e2
--- /dev/null
+++ b/target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.c
@@ -0,0 +1,156 @@
+/*
+ * arch/arm/mach-mx6/msi.c
+ *
+ * PCI MSI support for the imx processor
+ *
+ * Copyright (c) 2013, Boundary Devices.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <asm/bitops.h>
+#include <asm/mach/irq.h>
+#include <asm/irq.h>
+#include <linux/irqchip/chained_irq.h>
+
+#include "hardware.h"
+#include "msi.h"
+
+#define IMX_NUM_MSI_IRQS 128
+static DECLARE_BITMAP(msi_irq_in_use, IMX_NUM_MSI_IRQS);
+static int irq_base;
+
+static void imx_msi_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int i, j;
+ unsigned status;
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ irq_base = irq_alloc_descs(-1, 0, IMX_NUM_MSI_IRQS, 0);
+ if (irq_base < 0) {
+ printk(KERN_ERR "%s: could not allocate IRQ numbers\n", __func__);
+ return;
+ }
+
+ chained_irq_enter(chip, desc);
+ for (i = 0; i < 8; i++) {
+ status = imx_pcie_msi_pending(i);
+ while (status) {
+ j = __fls(status);
+ generic_handle_irq(irq_base + j);
+ status &= ~(1 << j);
+ }
+ irq_base += 32;
+ }
+ chained_irq_exit(chip, desc);
+}
+
+/*
+ * Dynamic irq allocate and deallocation
+ */
+int create_irq(void)
+{
+ int irq, pos;
+
+ do {
+ pos = find_first_zero_bit(msi_irq_in_use, IMX_NUM_MSI_IRQS);
+ if ((unsigned)pos >= IMX_NUM_MSI_IRQS)
+ return -ENOSPC;
+ /* test_and_set_bit operates on 32-bits at a time */
+ } while (test_and_set_bit(pos, msi_irq_in_use));
+
+ irq = irq_base + pos;
+ dynamic_irq_init(irq);
+ return irq;
+}
+
+void destroy_irq(unsigned int irq)
+{
+ int pos = irq - irq_base;
+
+ dynamic_irq_cleanup(irq);
+ clear_bit(pos, msi_irq_in_use);
+}
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+ destroy_irq(irq);
+}
+
+static void imx_msi_irq_ack(struct irq_data *d)
+{
+ return;
+}
+
+static void imx_msi_irq_enable(struct irq_data *d)
+{
+ imx_pcie_enable_irq(d->irq - irq_base, 1);
+ return unmask_msi_irq(d);
+}
+
+static void imx_msi_irq_disable(struct irq_data *d)
+{
+ imx_pcie_enable_irq(d->irq - irq_base, 0);
+ return mask_msi_irq(d);
+}
+
+static void imx_msi_irq_mask(struct irq_data *d)
+{
+ imx_pcie_mask_irq(d->irq - irq_base, 1);
+ return mask_msi_irq(d);
+}
+
+static void imx_msi_irq_unmask(struct irq_data *d)
+{
+ imx_pcie_mask_irq(d->irq - irq_base, 0);
+ return unmask_msi_irq(d);
+}
+
+static struct irq_chip imx_msi_chip = {
+ .name = "PCIe-MSI",
+ .irq_ack = imx_msi_irq_ack,
+ .irq_enable = imx_msi_irq_enable,
+ .irq_disable = imx_msi_irq_disable,
+ .irq_mask = imx_msi_irq_mask,
+ .irq_unmask = imx_msi_irq_unmask,
+};
+
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+ int irq = create_irq();
+ struct msi_msg msg;
+
+ if (irq < 0)
+ return irq;
+
+ irq_set_msi_desc(irq, desc);
+
+ msg.address_hi = 0x0;
+ msg.address_lo = MSI_MATCH_ADDR;
+ msg.data = (mxc_cpu_type << 15) | ((irq - irq_base) & 0xff);
+
+ write_msi_msg(irq, &msg);
+ irq_set_chip_and_handler(irq, &imx_msi_chip, handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ pr_info("%s: %d of %d\n", __func__, irq, NR_IRQS);
+ return 0;
+}
+
+void imx_msi_init(void)
+{
+ irq_set_chained_handler(MXC_INT_PCIE_0, imx_msi_handler);
+}
diff --git a/target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.h b/target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.h
new file mode 100644
index 0000000000..3f9be01adf
--- /dev/null
+++ b/target/linux/imx6/files-3.10/arch/arm/mach-imx/msi.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 Boundary Devices, Inc. All Rights Reserved.
+ *
+ * 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.
+ */
+
+void imx_pcie_enable_irq(unsigned pos, int set);
+void imx_pcie_mask_irq(unsigned pos, int set);
+unsigned imx_pcie_msi_pending(unsigned index);
+
+#define MSI_MATCH_ADDR 0x10000050
+#define MXC_INT_PCIE_0 152
+#define MXC_INT_PCIE_1 153
+#define MXC_INT_PCIE_2 154
+#define MXC_INT_PCIE_3 155
+
+void imx_msi_init(void);
diff --git a/target/linux/imx6/files-3.10/arch/arm/mach-imx/pcie.c b/target/linux/imx6/files-3.10/arch/arm/mach-imx/pcie.c
new file mode 100644
index 0000000000..44bd560c62
--- /dev/null
+++ b/target/linux/imx6/files-3.10/arch/arm/mach-imx/pcie.c
@@ -0,0 +1,1037 @@
+/*
+ * arch/arm/mach-imx/pcie.c
+ *
+ * PCIe host controller driver for IMX6 SOCs
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2013 Tim Harvey <tharvey@gateworks.com>
+ *
+ * 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.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+
+#include <asm/signal.h>
+#include <asm/mach/pci.h>
+#include <asm/sizes.h>
+
+#include "msi.h"
+
+/* PCIe Registers */
+#define PCIE_ARB_BASE_ADDR 0x01000000
+#define PCIE_ARB_END_ADDR 0x01FFFFFF
+#define PCIE_RC_IOBLSSR 0x1c
+
+/* Register Definitions */
+#define PRT_LOG_R_BaseAddress 0x700
+
+/* Register DB_R0 */
+/* Debug Register 0 */
+#define DB_R0 (PRT_LOG_R_BaseAddress + 0x28)
+#define DB_R0_RegisterSize 32
+#define DB_R0_RegisterResetValue 0x0
+#define DB_R0_RegisterResetMask 0xFFFFFFFF
+/* End of Register Definition for DB_R0 */
+
+/* Register DB_R1 */
+/* Debug Register 1 */
+#define DB_R1 (PRT_LOG_R_BaseAddress + 0x2c)
+#define DB_R1_RegisterSize 32
+#define DB_R1_RegisterResetValue 0x0
+#define DB_R1_RegisterResetMask 0xFFFFFFFF
+/* End of Register Definition for DB_R1 */
+
+#define PCIE_PL_MSICA 0x820
+#define PCIE_PL_MSICUA 0x824
+#define PCIE_PL_MSIC_INT 0x828
+
+#define MSIC_INT_EN 0x0
+#define MSIC_INT_MASK 0x4
+#define MSIC_INT_STATUS 0x8
+
+#define ATU_R_BaseAddress 0x900
+#define ATU_VIEWPORT_R (ATU_R_BaseAddress + 0x0)
+#define ATU_REGION_CTRL1_R (ATU_R_BaseAddress + 0x4)
+#define ATU_REGION_CTRL2_R (ATU_R_BaseAddress + 0x8)
+#define ATU_REGION_LOWBASE_R (ATU_R_BaseAddress + 0xC)
+#define ATU_REGION_UPBASE_R (ATU_R_BaseAddress + 0x10)
+#define ATU_REGION_LIMIT_ADDR_R (ATU_R_BaseAddress + 0x14)
+#define ATU_REGION_LOW_TRGT_ADDR_R (ATU_R_BaseAddress + 0x18)
+#define ATU_REGION_UP_TRGT_ADDR_R (ATU_R_BaseAddress + 0x1C)
+
+/* IOMUXC */
+#define IOMUXC_GPR_BASE_ADDR 0x020E0000
+#define IOMUXC_GPR1 (imx_pcie.gpr_base + 0x04)
+#define IOMUXC_GPR8 (imx_pcie.gpr_base + 0x20)
+#define IOMUXC_GPR12 (imx_pcie.gpr_base + 0x30)
+/* 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)
+/* 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]
+*/
+
+#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
+
+#define PCIE_CAP_STRUC_BaseAddress 0x70
+
+/* Register LNK_CAP */
+/* PCIE Link cap */
+#define LNK_CAP (PCIE_CAP_STRUC_BaseAddress + 0xc)
+
+/* End of Register Definitions */
+
+enum {
+ MemRdWr = 0,
+ MemRdLk = 1,
+ IORdWr = 2,
+ CfgRdWr0 = 4,
+ CfgRdWr1 = 5
+};
+
+struct imx_pcie_port {
+ u8 index;
+ u8 root_bus_nr;
+ void __iomem *base;
+ void __iomem *dbi_base;
+ spinlock_t conf_lock;
+
+ char io_space_name[16];
+ char mem_space_name[16];
+
+ struct resource res[2];
+ struct clk *clk;
+};
+
+struct imx_pcie_info {
+ struct imx_pcie_port imx_pcie_port[1];
+ int num_pcie_ports;
+
+ void __iomem *base;
+ void __iomem *dbi_base;
+ void __iomem *gpr_base;
+
+ unsigned int pcie_pwr_en;
+ unsigned int pcie_rst;
+ unsigned int pcie_wake_up;
+ unsigned int pcie_dis;
+};
+
+static struct imx_pcie_info imx_pcie;
+
+static int pcie_phy_cr_read(int addr, int *data);
+static int pcie_phy_cr_write(int addr, int data);
+static void change_field(int *in, int start, int end, int val);
+
+/* IMX PCIE GPR configure routines */
+static inline void imx_pcie_clrset(u32 mask, u32 val, void __iomem *addr)
+{
+ writel(((readl(addr) & ~mask) | (val & mask)), addr);
+}
+
+static int imx_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct imx_pcie_port *pp;
+
+ if (nr >= imx_pcie.num_pcie_ports)
+ return 0;
+
+ pp = &imx_pcie.imx_pcie_port[nr];
+ pp->root_bus_nr = sys->busnr;
+
+ /*
+ * IORESOURCE_IO
+ */
+ 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->res[0].name = pp->io_space_name;
+ if (pp->index == 0) {
+ pp->res[0].start = PCIE_ARB_BASE_ADDR;
+ pp->res[0].end = pp->res[0].start + SZ_1M - 1;
+ }
+ pp->res[0].flags = IORESOURCE_IO;
+ if (request_resource(&ioport_resource, &pp->res[0]))
+ panic("Request PCIe IO resource failed\n");
+ pci_add_resource_offset(&sys->resources, &pp->res[0], sys->io_offset);
+
+ /*
+ * 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->res[1].name = pp->mem_space_name;
+ if (pp->index == 0) {
+ pp->res[1].start = PCIE_ARB_BASE_ADDR + SZ_1M;
+ pp->res[1].end = pp->res[1].start + SZ_16M - SZ_2M - 1;
+ }
+ pp->res[1].flags = IORESOURCE_MEM;
+ if (request_resource(&iomem_resource, &pp->res[1]))
+ panic("Request PCIe Memory resource failed\n");
+ pci_add_resource_offset(&sys->resources, &pp->res[1], sys->mem_offset);
+
+ return 1;
+}
+
+static int imx_pcie_link_up(void __iomem *dbi_base)
+{
+ /* Check the pcie link up or link down */
+ int iterations = 200;
+ u32 rc, ltssm, rx_valid, temp;
+
+ do {
+ /* link is debug bit 36 debug 1 start in bit 32 */
+ rc = readl(dbi_base + DB_R1) & (0x1 << (36 - 32)) ;
+ iterations--;
+ 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
+ */
+ pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_ASIC_OUT, &rx_valid);
+ ltssm = readl(dbi_base + DB_R0) & 0x3F;
+ if ((ltssm == 0x0D) && ((rx_valid & 0x01) == 0)) {
+ pr_info("Transition to gen2 is stuck, reset PHY!\n");
+ pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp);
+ change_field(&temp, 3, 3, 0x1);
+ change_field(&temp, 5, 5, 0x1);
+ pcie_phy_cr_write(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO,
+ 0x0028);
+ usleep_range(2000, 3000);
+ pcie_phy_cr_read(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(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO,
+ 0x0000);
+ }
+
+ if ((iterations < 0))
+ pr_info("link up failed, DB_R0:0x%08x, DB_R1:0x%08x!\n"
+ , readl(dbi_base + DB_R0)
+ , readl(dbi_base + DB_R1));
+ } while (!rc && iterations);
+
+ if (!rc)
+ return 0;
+ return 1;
+}
+
+static void imx_pcie_regions_setup(void __iomem *dbi_base)
+{
+ unsigned bus;
+ unsigned i;
+ unsigned untranslated_base = PCIE_ARB_END_ADDR +1 - SZ_1M;
+ void __iomem *p = dbi_base + PCIE_PL_MSIC_INT;
+ /*
+ * 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-3 outbound used to access target cfg
+ */
+ for (bus = 1; bus <= 4; bus++) {
+ writel(bus - 1, dbi_base + ATU_VIEWPORT_R);
+ writel(untranslated_base, dbi_base + ATU_REGION_LOWBASE_R);
+ untranslated_base += (1 << 18);
+ if (bus == 4)
+ untranslated_base -= (1 << 14); //(remove registers)
+ writel(untranslated_base - 1, dbi_base + ATU_REGION_LIMIT_ADDR_R);
+ writel(0, dbi_base + ATU_REGION_UPBASE_R);
+
+ writel(bus << 24, dbi_base + ATU_REGION_LOW_TRGT_ADDR_R);
+ writel(0, dbi_base + ATU_REGION_UP_TRGT_ADDR_R);
+ writel((bus > 1) ? CfgRdWr1 : CfgRdWr0,
+ dbi_base + ATU_REGION_CTRL1_R);
+ writel((1<<31), dbi_base + ATU_REGION_CTRL2_R);
+ }
+
+ writel(MSI_MATCH_ADDR, dbi_base + PCIE_PL_MSICA);
+ writel(0, dbi_base + PCIE_PL_MSICUA);
+ for (i = 0; i < 8 ; i++) {
+ writel(0, p + MSIC_INT_EN);
+ writel(0xffffffff, p + MSIC_INT_MASK);
+ writel(0xffffffff, p + MSIC_INT_STATUS);
+ p += 12;
+ }
+}
+
+void imx_pcie_mask_irq(unsigned pos, int set)
+{
+ unsigned mask = 1 << (pos & 0x1f);
+ unsigned val, newval;
+ void __iomem *p = imx_pcie.dbi_base + PCIE_PL_MSIC_INT + MSIC_INT_MASK + ((pos >> 5) * 12);
+ if (pos >= (8 * 32))
+ return;
+ val = readl(p);
+ if (set)
+ newval = val | mask;
+ else
+ newval = val & ~mask;
+ if (val != newval)
+ writel(newval, p);
+}
+
+void imx_pcie_enable_irq(unsigned pos, int set)
+{
+ unsigned mask = 1 << (pos & 0x1f);
+ unsigned val, newval;
+ void __iomem *p = imx_pcie.dbi_base + PCIE_PL_MSIC_INT + MSIC_INT_EN + ((pos >> 5) * 12);
+ if (pos >= (8 * 32))
+ return;
+ val = readl(p);
+ if (set)
+ newval = val | mask;
+ else
+ newval = val & ~mask;
+ if (val != newval)
+ writel(newval, p);
+ if (set && (val != newval))
+ imx_pcie_mask_irq(pos, 0); /* unmask when enabled */
+}
+
+unsigned imx_pcie_msi_pending(unsigned index)
+{
+ unsigned val, mask;
+ void __iomem *p = imx_pcie.dbi_base + PCIE_PL_MSIC_INT + (index * 12);
+ if (index >= 8)
+ return 0;
+ val = readl(p + MSIC_INT_STATUS);
+ mask = readl(p + MSIC_INT_MASK);
+ val &= ~mask;
+ writel(val, p + MSIC_INT_STATUS);
+ return val;
+}
+
+static char master_abort(struct pci_bus *bus, u32 devfn, int where)
+{
+ u32 reg;
+ void __iomem *dbi_base = imx_pcie.dbi_base;
+ int ret = 0;
+
+ reg = readl(dbi_base + PCIE_RC_IOBLSSR);
+ if (reg & 0x71000000) {
+ if (reg & 1<<30)
+ pr_err("%d:%02d.%d 0x%04x: parity error\n", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+ if (reg & 1<<29) {
+ pr_err("%d:%02d.%d 0x%04x: master abort\n", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+ ret = 1;
+ }
+ if (reg & 1<<28)
+ pr_err("%d:%02d.%d 0x%04x: target abort\n", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+ if (reg & 1<<24)
+ pr_err("%d:%02d.%d 0x%04x: master data parity error\n", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+ writel(reg, dbi_base + PCIE_RC_IOBLSSR);
+ udelay(1500); // without this delay subsequent reads through bridge can erroneously return 0???
+ }
+ return ret;
+}
+
+static volatile void *get_cfg_addr(struct pci_bus *bus, u32 devfn, int where)
+{
+ unsigned busnum;
+ void __iomem *base = imx_pcie.base;
+ void __iomem *dbi_base = imx_pcie.dbi_base;
+
+ if (!bus->number) {
+ if (devfn != 0)
+ return 0;
+ return (imx_pcie.dbi_base) + (where & 0x0ffc);
+ }
+ if ((devfn > 0xff) || (bus->number > 15))
+ return 0;
+ busnum = bus->number - 1;
+ if ((busnum < 3) && (devfn <= 3)) {
+ return (base) + (busnum << 18) + (devfn << 16) + (where & 0xfffc);
+ }
+ writel(3, dbi_base + ATU_VIEWPORT_R);
+ writel((bus->number << 24) | (devfn << 16),
+ dbi_base + ATU_REGION_LOW_TRGT_ADDR_R);
+ writel((bus->number > 1) ? CfgRdWr1 : CfgRdWr0,
+ dbi_base + ATU_REGION_CTRL1_R);
+ return (base) + (3 << 18) + (where & 0xfffc);
+}
+
+static int imx_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ const volatile void *va_address;
+ u32 v;
+
+ if (0)
+ pr_info("%s: bus=%x, devfn=%x, where=%x size=%x\n", __func__, bus->number, devfn, where, size);
+ va_address = get_cfg_addr(bus, devfn, where);
+ if (!va_address) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ v = readl(va_address);
+ if (master_abort(bus, devfn, where)) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ if (0)
+ pr_info("%s: bus=%x, devfn=%x, where=%x size=%x v=%x\n", __func__, bus->number, devfn, where, size, v);
+ if (size == 4) {
+ *val = v;
+ } else if (size == 1) {
+ *val = (v >> (8 * (where & 3))) & 0xFF;
+ } else if (size == 2) {
+ *val = (v >> (8 * (where & 3))) & 0xFFFF;
+ } else {
+ *val = 0xffffffff;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int imx_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ volatile void *va_address;
+ u32 mask, tmp;
+
+ if (0)
+ pr_info("%s: bus=%x, devfn=%x, where=%x size=%x val=%x\n", __func__, bus->number, devfn, where, size, val);
+ va_address = get_cfg_addr(bus, devfn, where);
+ if (!va_address)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ if (size == 4) {
+ writel(val, va_address);
+ return (master_abort(bus, devfn, where))
+ ?PCIBIOS_DEVICE_NOT_FOUND:PCIBIOS_SUCCESSFUL;
+ }
+ if (size == 2)
+ mask = ~(0xFFFF << ((where & 0x3) * 8));
+ else if (size == 1)
+ mask = ~(0xFF << ((where & 0x3) * 8));
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ tmp = readl(va_address) & mask;
+ tmp |= val << ((where & 0x3) * 8);
+ writel(tmp, va_address);
+ return (master_abort(bus, devfn, where))
+ ?PCIBIOS_DEVICE_NOT_FOUND:PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops imx_pcie_ops = {
+ .read = imx_pcie_rd_conf,
+ .write = imx_pcie_wr_conf,
+};
+
+signed short irq_map[] = {
+ -EINVAL,
+ MXC_INT_PCIE_3, /* int a */
+ MXC_INT_PCIE_2, /* int b */
+ MXC_INT_PCIE_1, /* int c */
+ MXC_INT_PCIE_0, /* int d/MSI */
+};
+
+static int imx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ int val = -EINVAL;
+ if (pin <= 4)
+ val = irq_map[pin];
+ return val;
+}
+
+static struct hw_pci imx_pci __initdata = {
+ .nr_controllers = 1,
+ .setup = imx_pcie_setup,
+ .ops = &imx_pcie_ops,
+ .map_irq = imx_pcie_map_irq,
+};
+
+/* PHY CR bus acess routines */
+static int pcie_phy_cr_ack_polling(int max_iterations, int exp_val)
+{
+ u32 temp_rd_data, wait_counter = 0;
+
+ do {
+ temp_rd_data = readl(imx_pcie.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(int addr)
+{
+ u32 temp_wr_data;
+ void __iomem *dbi_base = imx_pcie.dbi_base;
+
+ /* 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(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(100, 0))
+ return 0 ;
+
+ return 1 ;
+}
+
+static int pcie_phy_cr_read(int addr , int *data)
+{
+ u32 temp_rd_data, temp_wr_data;
+ void __iomem *dbi_base = imx_pcie.dbi_base;
+
+ /* write addr */
+ /* cap addr */
+ if (!pcie_phy_cr_cap_addr(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(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 */
+ if (!pcie_phy_cr_ack_polling(100, 0))
+ return 0 ;
+
+ return 1 ;
+
+}
+
+static int pcie_phy_cr_write(int addr, int data)
+{
+ u32 temp_wr_data;
+ void __iomem *dbi_base = imx_pcie.dbi_base;
+
+ /* write addr */
+ /* cap addr */
+ if (!pcie_phy_cr_cap_addr(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(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(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(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(100, 0))
+ return 0;
+
+ temp_wr_data = 0x0 ;
+ writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+ return 1 ;
+}
+
+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 int imx_pcie_enable_controller(struct device *dev)
+{
+ struct clk *clk;
+ struct device_node *np = dev->of_node;
+
+ if (gpio_is_valid(imx_pcie.pcie_pwr_en)) {
+ /* Enable PCIE power */
+ gpio_request(imx_pcie.pcie_pwr_en, "PCIE POWER_EN");
+
+ /* activate PCIE_PWR_EN */
+ gpio_direction_output(imx_pcie.pcie_pwr_en, 1);
+ }
+
+ // power up PCIe PHY
+ imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 0 << 18, IOMUXC_GPR1);
+
+ /* enable the clks */
+ if (np)
+ clk = of_clk_get(np, 0);
+ else
+ clk = devm_clk_get(dev, "pcie_clk");
+ if (IS_ERR(clk)) {
+ pr_err("no pcie clock.\n");
+ return -EINVAL;
+ }
+
+ if (clk_prepare_enable(clk)) {
+ pr_err("can't enable pcie clock.\n");
+ clk_put(clk);
+ return -EINVAL;
+ }
+
+ // Enable PCIe PHY ref clock
+ imx_pcie_clrset(iomuxc_gpr1_pcie_ref_clk_en, 1 << 16, IOMUXC_GPR1);
+
+ return 0;
+}
+
+static void card_reset(struct device *dev)
+{
+ if (gpio_is_valid(imx_pcie.pcie_rst)) {
+ /* PCIE RESET */
+ gpio_request(imx_pcie.pcie_rst, "PCIE RESET");
+
+ /* activate PERST_B */
+ gpio_direction_output(imx_pcie.pcie_rst, 0);
+
+ /* Add one reset to the pcie external device */
+ msleep(100);
+
+ /* deactive PERST_B */
+ gpio_direction_output(imx_pcie.pcie_rst, 1);
+ }
+}
+
+static void add_pcie_port(struct platform_device *pdev, void __iomem *base, void __iomem *dbi_base)
+{
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (imx_pcie_link_up(dbi_base)) {
+ struct imx_pcie_port *pp = &imx_pcie.imx_pcie_port[imx_pcie.num_pcie_ports++];
+
+ pr_info("IMX PCIe port: link up.\n");
+
+ pp->index = 0;
+ pp->root_bus_nr = -1;
+ pp->base = base;
+ pp->dbi_base = dbi_base;
+ spin_lock_init(&pp->conf_lock);
+ memset(pp->res, 0, sizeof(pp->res));
+ } else {
+ pr_info("IMX PCIe port: link down!\n");
+ /* Release the clocks, and disable the power */
+
+ if (np)
+ clk = of_clk_get(np, 0);
+ else
+ clk = clk_get(NULL, "pcie_clk");
+ if (IS_ERR(clk)) {
+ pr_err("no pcie clock.\n");
+ return;
+ }
+
+ clk_disable_unprepare(clk);
+ clk_put(clk);
+
+ // Disable the PCIE PHY Ref Clock
+ imx_pcie_clrset(iomuxc_gpr1_pcie_ref_clk_en, 0 << 16, IOMUXC_GPR1);
+
+ if (gpio_is_valid(imx_pcie.pcie_pwr_en)) {
+ /* Disable PCIE power */
+ gpio_request(imx_pcie.pcie_pwr_en, "PCIE POWER_EN");
+
+ /* activate PCIE_PWR_EN */
+ gpio_direction_output(imx_pcie.pcie_pwr_en, 0);
+ }
+
+ // Power down PCIE PHY
+ imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 1 << 18, IOMUXC_GPR1);
+ }
+}
+
+static int imx_pcie_abort_handler(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
+{
+ unsigned long instr;
+ unsigned long pc = instruction_pointer(regs) - 4;
+
+ instr = *(unsigned long *)pc;
+/* imprecise aborts are no longer enabled in 3.7+ during init it would appear.
+ * We now using PCIE_RC_IOBLSSR to detect master abort however we will still get
+ * at least one imprecise abort and need to have a handler.
+ */
+#if 0
+ if (instr == 0xf57ff04f) {
+ /* dsb sy */
+ pc -= 4;
+ instr = *(unsigned long *)pc;
+ }
+ pr_info("PCIe abort: address = 0x%08lx fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx instr=%08lx\n",
+ addr, fsr, regs->ARM_pc, regs->ARM_lr, instr);
+
+
+ /*
+ * If the instruction being executed was a read,
+ * make it look like it read all-ones.
+ */
+ if ((instr & 0x0c500000) == 0x04100000) {
+ /* LDR instruction */
+ int reg = (instr >> 12) & 15;
+
+ regs->uregs[reg] = -1;
+ regs->ARM_pc = pc + 4;
+ return 0;
+ }
+ return 1;
+#else
+ pr_info("PCIe abort: address = 0x%08lx fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx instr=%08lx\n",
+ addr, fsr, regs->ARM_pc, regs->ARM_lr, instr);
+
+ return 0;
+#endif
+}
+
+
+static int imx_pcie_pltfm_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource res;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No of data found\n");
+ return -EINVAL;
+ }
+
+ res.start = res.end = 0;
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret)
+ goto err;
+ mem = &res;
+ imx_pcie.pcie_pwr_en = of_get_named_gpio(np, "pwren-gpios", 0);
+ imx_pcie.pcie_rst = of_get_named_gpio(np, "rst-gpios", 0);
+ imx_pcie.pcie_wake_up = of_get_named_gpio(np, "wake-gpios", 0);
+ imx_pcie.pcie_dis = of_get_named_gpio(np, "dis-gpios", 0);
+ //pdev->dev.platform_data = pdata;
+
+ imx_pcie.base = ioremap_nocache(PCIE_ARB_END_ADDR - SZ_1M + 1, SZ_1M - SZ_16K);
+ if (!imx_pcie.base) {
+ pr_err("error with ioremap in function %s\n", __func__);
+ return -EIO;
+ }
+
+ imx_pcie.dbi_base = devm_ioremap(dev, mem->start, resource_size(mem));
+ if (!imx_pcie.dbi_base) {
+ dev_err(dev, "can't map %pR\n", mem);
+ return -ENOMEM;
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-iomuxc-gpr");
+ if (!np) {
+ dev_err(dev, "can't find iomux\n");
+ return -ENOMEM;
+ }
+ ret = of_address_to_resource(np, 0, &res);
+ of_node_put(np);
+ if (ret)
+ goto err;
+ mem = &res;
+ imx_pcie.gpr_base = devm_ioremap(dev, mem->start, resource_size(mem));
+ if (!imx_pcie.gpr_base) {
+ dev_err(dev, "can't map %pR\n", mem);
+ return -ENOMEM;
+ }
+
+ // hold LTSSM in detect state
+ imx_pcie_clrset(iomuxc_gpr12_app_ltssm_enable, 0 << 10, IOMUXC_GPR12);
+
+ /* configure constant input signal to the pcie ctrl and phy */
+ // set device type to RC (PCI_EXP_TYPE_ROOT_PORT=4 is from pcie_regs.h)
+ imx_pcie_clrset(iomuxc_gpr12_device_type, PCI_EXP_TYPE_ROOT_PORT << 12, IOMUXC_GPR12);
+ // loss of signal detect sensitivity function - must be 0x9
+ imx_pcie_clrset(iomuxc_gpr12_los_level, 9 << 4, IOMUXC_GPR12);
+ // not clear what values these should have from RM
+ imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen1, 0 << 0, IOMUXC_GPR8);
+ imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_3p5db, 0 << 6, IOMUXC_GPR8);
+ imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_6db, 20 << 12, IOMUXC_GPR8);
+ imx_pcie_clrset(iomuxc_gpr8_tx_swing_full, 127 << 18, IOMUXC_GPR8);
+ imx_pcie_clrset(iomuxc_gpr8_tx_swing_low, 127 << 25, IOMUXC_GPR8);
+
+ /* Enable the pwr, clks and so on */
+ ret = imx_pcie_enable_controller(dev);
+ if (ret)
+ goto err;
+
+ /* togle the external card's reset */
+ card_reset(dev) ;
+
+ usleep_range(3000, 4000);
+ imx_pcie_regions_setup(imx_pcie.dbi_base);
+ usleep_range(3000, 4000);
+
+ /*
+ * Force to GEN1 because of PCIE2USB storage stress tests
+ * would be failed when GEN2 is enabled
+ */
+ writel(((readl(imx_pcie.dbi_base + LNK_CAP) & 0xfffffff0) | 0x1),
+ imx_pcie.dbi_base + LNK_CAP);
+
+ /* start link up */
+ imx_pcie_clrset(iomuxc_gpr12_app_ltssm_enable, 1 << 10, IOMUXC_GPR12);
+
+ hook_fault_code(16 + 6, imx_pcie_abort_handler, SIGBUS, 0,
+ "imprecise external abort");
+
+ /* add the pcie port */
+ add_pcie_port(pdev, imx_pcie.base, imx_pcie.dbi_base);
+
+ pci_common_init(&imx_pci);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int imx_pcie_pltfm_remove(struct platform_device *pdev)
+{
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* Release clocks, and disable power */
+ if (np)
+ clk = of_clk_get(np, 0);
+ else
+ clk = devm_clk_get(dev, "pcie_clk");
+ if (IS_ERR(clk))
+ pr_err("no pcie clock.\n");
+
+ if (clk) {
+ clk_disable_unprepare(clk);
+ clk_put(clk);
+ }
+
+ // disable PCIe PHY clock ref
+ imx_pcie_clrset(iomuxc_gpr1_pcie_ref_clk_en, 0 << 16, IOMUXC_GPR1);
+
+ if (gpio_is_valid(imx_pcie.pcie_pwr_en)) {
+ /* Disable PCIE power */
+ gpio_request(imx_pcie.pcie_pwr_en, "PCIE POWER_EN");
+
+ /* activate PCIE_PWR_EN */
+ gpio_direction_output(imx_pcie.pcie_pwr_en, 0);
+ }
+
+ // power down PCIe PHY
+ imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 1 << 18, IOMUXC_GPR1);
+
+ iounmap(imx_pcie.base);
+ iounmap(imx_pcie.dbi_base);
+ iounmap(imx_pcie.gpr_base);
+ release_mem_region(iomem->start, resource_size(iomem));
+ //platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id of_imx_pcie_match[] = {
+ { .compatible = "fsl,pcie" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_imx_pcie_match);
+
+static struct platform_driver imx_pcie_pltfm_driver = {
+ .driver = {
+ .name = "imx-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = of_imx_pcie_match,
+ },
+ .probe = imx_pcie_pltfm_probe,
+ .remove = imx_pcie_pltfm_remove,
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init imx_pcie_drv_init(void)
+{
+ pcibios_min_io = 0;
+ pcibios_min_mem = 0;
+
+ 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);
+late_initcall(imx_pcie_drv_init);
+
+MODULE_DESCRIPTION("i.MX PCIE platform driver");
+MODULE_LICENSE("GPL v2");