From a417b7a6e486fe5c5792b3cdf002b0261e421f21 Mon Sep 17 00:00:00 2001 From: Yue Wang Date: Fri, 12 May 2017 09:55:03 +0800 Subject: [PATCH] pcie: add pcie driver for axg PD#142470: add pcie driver for axg 1. update dts of skt&s400 2. update pcie driver Change-Id: Ic569e8fed1a4434e54847580299703f0fe07cd9b Signed-off-by: Yue Wang --- MAINTAINERS | 7 + arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts | 60 ++ arch/arm64/boot/dts/amlogic/axg_pxp.dts | 60 ++ arch/arm64/boot/dts/amlogic/axg_s400.dts | 59 ++ arch/arm64/boot/dts/amlogic/mesonaxg.dtsi | 1 + arch/arm64/configs/meson64_defconfig | 32 + drivers/amlogic/Kconfig | 2 + drivers/amlogic/Makefile | 2 + drivers/amlogic/pci/Kconfig | 14 + drivers/amlogic/pci/Makefile | 5 + drivers/amlogic/pci/pcie-amlogic.c | 625 ++++++++++++++++++ drivers/amlogic/pci/pcie-amlogic.h | 48 ++ 12 files changed, 915 insertions(+) create mode 100644 drivers/amlogic/pci/Kconfig create mode 100644 drivers/amlogic/pci/Makefile create mode 100644 drivers/amlogic/pci/pcie-amlogic.c create mode 100644 drivers/amlogic/pci/pcie-amlogic.h diff --git a/MAINTAINERS b/MAINTAINERS index 09a1fe47ea35..aacc4c77b10a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13901,3 +13901,10 @@ M: Yun Cai F: arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts F: arch/arm64/boot/dts/amlogic/axg_s400.dts F: arch/arm64/boot/dts/amlogic/axg_s420.dts + +AMLOGIC PCIE DRIVER SUPPORT +M: Yue Wang +F: drivers/amlogic/pci/pcie-amlogic.c +F: drivers/amlogic/pci/pcie-amlogic.h +F: drivers/amlogic/pci/Kconfig +F: drivers/amlogic/pci/Makefile diff --git a/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts b/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts index be17aa5fec19..96ed23b3ba83 100644 --- a/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts +++ b/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts @@ -56,6 +56,66 @@ }; }; + pcie_A: pcieA@f9800000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xf9800000 0x0 0x400000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xff646000 0x0 0x2000 + 0x0 0xffd01080 0x0 0x10 + 0x0 0xf9c00000 0x0 0x100000>; + reg-names = "elbi", "phy", "cfg", "reset", "config"; + reset-gpio = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>; + interrupts = <0 177 0>, <0 179 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xf9d00000 0x0 0x10000 + /*downstream I/O */ + 0x82000000 0 0xf9d10000 0x0 0xf9d10000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <1>; + + clocks = <&clkc CLKID_PCIE_PLL + &clkc CLKID_PCIE_A>; + clock-names = "pcie_refpll", + "pcie_a"; + board-type = <0>;/*0:skt 1:s400*/ + status = "okay"; + }; + + pcie_B: pcieB@fa000000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xfa000000 0x0 0x400000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xff648000 0x0 0x2000 + 0x0 0xffd01080 0x0 0x10 + 0x0 0xfa400000 0x0 0x100000>; + reg-names = "elbi", "phy", "cfg", "reset", "config"; + reset-gpio = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>; + interrupts = <0 167 0>, <0 169 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xfa500000 0x0 0x10000 + /* downstream I/O */ + 0x82000000 0 0xfa510000 0x0 0xfa510000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <2>; + + clocks = <&clkc CLKID_PCIE_PLL + &clkc CLKID_PCIE_B>; + clock-names = "pcie_refpll", + "pcie_b"; + board-type = <0>;/*0:skt 1:s400*/ + status = "okay"; + }; + }; /* end of / */ diff --git a/arch/arm64/boot/dts/amlogic/axg_pxp.dts b/arch/arm64/boot/dts/amlogic/axg_pxp.dts index 834a81962828..04cacd400fd1 100644 --- a/arch/arm64/boot/dts/amlogic/axg_pxp.dts +++ b/arch/arm64/boot/dts/amlogic/axg_pxp.dts @@ -57,5 +57,65 @@ }; + + + pcie_A: pcie@f9800000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xf9800000 0x0 0x400000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xff646000 0x0 0x2000 + 0x0 0xffd01080 0x0 0x10 + 0x0 0xf9c00000 0x0 0x100000>; + reg-names = "elbi", "phy", "cfg", "reset", "config"; + reset-gpio = <&gpio GPIOX_19 0>; + interrupts = <0 177 0>, <0 179 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xf9d00000 0x0 0x10000 + /*downstream I/O */ + 0x82000000 0 0xf9d10000 0x0 0xf9d10000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <1>; + + //clocks = <&clkc CLKID_PCIE_REFPLL + //&clkc CLKID_PCIE_A>; + //clock-names = "pcie_refpll", + // "pcie_a"; + + status = "disable"; + }; + pcie_B: pcie@fa000000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xfa000000 0x0 0x400000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xff648000 0x0 0x2000 + 0x0 0xffd01080 0x0 0x10 + 0x0 0xfa400000 0x0 0x100000>; + reg-names = "elbi", "phy-unuse", "cfg", "reset-unuse", "config"; + interrupts = <0 167 0>, <0 169 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xfa500000 0x0 0x10000 + /* downstream I/O */ + 0x82000000 0 0xfa510000 0x0 0xfa510000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <2>; + + //clocks = <&clkc CLKID_PCIE_REFPLL + // &clkc CLKID_PCIE_B>; + //clock-names = "pcie_refpll", + // "pcie_b"; + + status = "disable"; + }; + }; /* end of / */ diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index 0d25a3605234..a13664e106bb 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -54,7 +54,66 @@ reg = <0x0 0x05300000 0x0 0x2000000>; no-map; }; + }; + pcie_A: pcieA@f9800000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xf9800000 0x0 0x400000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xff646000 0x0 0x2000 + 0x0 0xffd01080 0x0 0x10 + 0x0 0xf9c00000 0x0 0x100000>; + reg-names = "elbi", "phy", "cfg", "reset", "config"; + reset-gpio = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>; + interrupts = <0 177 0>, <0 179 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xf9d00000 0x0 0x10000 + /*downstream I/O */ + 0x82000000 0 0xf9d10000 0x0 0xf9d10000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <1>; + + clocks = <&clkc CLKID_PCIE_PLL + &clkc CLKID_PCIE_A>; + clock-names = "pcie_refpll", + "pcie_a"; + board-type = <1>;/*0: skt 1: s400*/ + status = "okay"; + }; + + pcie_B: pcieB@fa000000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xfa000000 0x0 0x400000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xff648000 0x0 0x2000 + 0x0 0xffd01080 0x0 0x10 + 0x0 0xfa400000 0x0 0x100000>; + reg-names = "elbi", "phy", "cfg", "reset", "config"; + reset-gpio = <&gpio GPIOZ_10 GPIO_ACTIVE_HIGH>; + interrupts = <0 167 0>, <0 169 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xfa500000 0x0 0x10000 + /* downstream I/O */ + 0x82000000 0 0xfa510000 0x0 0xfa510000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <2>; + + clocks = <&clkc CLKID_PCIE_PLL + &clkc CLKID_PCIE_B>; + clock-names = "pcie_refpll", + "pcie_b"; + board-type = <1>;/*0:skt 1:s400*/ + status = "okay"; }; }; /* end of / */ diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi index 94051f75b9b2..9692fd7c9d1f 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -20,6 +20,7 @@ #include #include #include +#include / { cpus:cpus { diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 645bb29699a5..3c40036ad51f 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -19,6 +19,37 @@ CONFIG_PROFILING=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y +# +# Bus support +# +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_SYSCALL=y +# CONFIG_PCIEPORTBUS is not set +CONFIG_PCI_BUS_ADDR_T_64BIT=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_PRI is not set +# CONFIG_PCI_PASID is not set +CONFIG_PCI_LABEL=y +# CONFIG_HOTPLUG_PCI is not set +# +# PCI host controller drivers +# +CONFIG_PCIE_DW_PLAT=y +CONFIG_PCIE_DW=y +CONFIG_8139CP=y +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +CONFIG_8139TOO_TUNE_TWISTER=y +CONFIG_8139TOO_8129=y +CONFIG_8139_OLD_RX_RESET=y +CONFIG_R8169=y CONFIG_SCHED_MC=y CONFIG_NR_CPUS=8 CONFIG_PREEMPT=y @@ -260,6 +291,7 @@ CONFIG_AMLOGIC_WDT_MESON=y CONFIG_AMLOGIC_WIFI=y CONFIG_AMLOGIC_BT_DEVICE=y CONFIG_AMLOGIC_DVB_COMPAT=y +CONFIG_AMLOGIC_PCIE=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 8cd28596a6e9..ecb21ba80711 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -100,5 +100,7 @@ source "drivers/amlogic/dvb/Kconfig" source "drivers/amlogic/power/Kconfig" +source "drivers/amlogic/pci/Kconfig" + endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index df9676276d61..663e0b74c534 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -83,3 +83,5 @@ obj-$(CONFIG_AMLOGIC_BT_DEVICE) += bluetooth/ obj-$(CONFIG_AMLOGIC_WIFI) += wifi/ obj-$(CONFIG_AMLOGIC_POWER) += power/ + +obj-$(CONFIG_AMLOGIC_PCIE) += pci/ diff --git a/drivers/amlogic/pci/Kconfig b/drivers/amlogic/pci/Kconfig new file mode 100644 index 000000000000..c09333fa57bc --- /dev/null +++ b/drivers/amlogic/pci/Kconfig @@ -0,0 +1,14 @@ +menu "Multimedia Card support" + +config AMLOGIC_PCIE + bool "Amlogic Multimedia Card support" + depends on PCI + depends on PCI_MSI_IRQ_DOMAIN + help + This selects support for the Amlogic PCIE Host Controller + found on the A113 family of SoCs. This controller is + GEN2 compliant. + If you have a controller with this interface, say Y here. + + +endmenu diff --git a/drivers/amlogic/pci/Makefile b/drivers/amlogic/pci/Makefile new file mode 100644 index 000000000000..22f19e675e18 --- /dev/null +++ b/drivers/amlogic/pci/Makefile @@ -0,0 +1,5 @@ +# +# Amlogic PCIE specific Makefile +# + +obj-$(CONFIG_AMLOGIC_PCIE) += pcie-amlogic.o diff --git a/drivers/amlogic/pci/pcie-amlogic.c b/drivers/amlogic/pci/pcie-amlogic.c new file mode 100644 index 000000000000..02425b4b58c3 --- /dev/null +++ b/drivers/amlogic/pci/pcie-amlogic.c @@ -0,0 +1,625 @@ +/* + * drivers/amlogic/pci/pcie-amlogic.c + * + * Copyright (C) 2017 Amlogic, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../drivers/pci/host/pcie-designware.h" +#include "pcie-amlogic.h" + +struct amlogic_pcie { + struct pcie_port pp; + void __iomem *elbi_base; /* DT 0th resource */ + void __iomem *phy_base; /* DT 1st resource */ + void __iomem *cfg_base; /* DT 2nd resource */ + void __iomem *reset_base;/* DT 3nd resource */ + int reset_gpio; + struct clk *clk; + struct clk *bus_clk; + int pcie_num; + int board_type; +}; + +#define to_amlogic_pcie(x) container_of(x, struct amlogic_pcie, pp) +struct amlogic_pcie *g_amlogic_pcie; + +static void amlogic_elb_writel(struct amlogic_pcie *amlogic_pcie, u32 val, + u32 reg) +{ + writel(val, amlogic_pcie->elbi_base + reg); +} + +static u32 amlogic_elb_readl(struct amlogic_pcie *amlogic_pcie, u32 reg) +{ + return readl(amlogic_pcie->elbi_base + reg); +} + +static void amlogic_cfg_writel(struct amlogic_pcie *amlogic_pcie, u32 val, + u32 reg) +{ + writel(val, amlogic_pcie->cfg_base + reg); +} + +static u32 amlogic_cfg_readl(struct amlogic_pcie *amlogic_pcie, u32 reg) +{ + return readl(amlogic_pcie->cfg_base + reg); +} + +static void amlogic_pcie_assert_reset(struct amlogic_pcie *amlogic_pcie) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + struct device *dev = pp->dev; + + if (amlogic_pcie->board_type == 1) { + if (amlogic_pcie->pcie_num == 2) { + if (amlogic_pcie->reset_gpio >= 0) { + devm_gpio_request_one(dev, + amlogic_pcie->reset_gpio, + GPIOF_OUT_INIT_HIGH, "RESET"); + } + + if (gpio_is_valid(amlogic_pcie->reset_gpio)) { + dev_info(amlogic_pcie->pp.dev, + "GPIOZ_10: amlogic_pcie_assert_reset\n"); + gpio_direction_output( + amlogic_pcie->reset_gpio, 0); + msleep(100); + gpio_direction_input( + amlogic_pcie->reset_gpio); + } + } else { + if (amlogic_pcie->reset_gpio >= 0) { + devm_gpio_request_one(dev, + amlogic_pcie->reset_gpio, + GPIOF_OUT_INIT_HIGH, "RESET"); + } + + if (gpio_is_valid(amlogic_pcie->reset_gpio)) { + dev_info(amlogic_pcie->pp.dev, + "GPIOX_19: amlogic_pcie_assert_reset\n"); + gpio_set_value_cansleep( + amlogic_pcie->reset_gpio, 0); + msleep(100); + gpio_set_value_cansleep( + amlogic_pcie->reset_gpio, 1); + } + } + } else { + if (amlogic_pcie->pcie_num == 1) { + if (amlogic_pcie->reset_gpio >= 0) { + devm_gpio_request_one(dev, + amlogic_pcie->reset_gpio, + GPIOF_OUT_INIT_HIGH, "RESET"); + } + + if (gpio_is_valid(amlogic_pcie->reset_gpio)) { + dev_info(amlogic_pcie->pp.dev, + "GPIOX_19: amlogic_pcie_assert_reset\n"); + gpio_set_value_cansleep( + amlogic_pcie->reset_gpio, 0); + msleep(100); + gpio_set_value_cansleep( + amlogic_pcie->reset_gpio, 1); + } + } + } +} + +void amlogic_set_max_payload(struct amlogic_pcie *amlogic_pcie, int size) +{ + int max_payload_size = 1; + u32 val = 0; + + switch (size) { + case 128: + max_payload_size = 0; + break; + case 256: + max_payload_size = 1; + break; + case 512: + max_payload_size = 2; + break; + case 1024: + max_payload_size = 3; + break; + case 2048: + max_payload_size = 4; + break; + case 4096: + max_payload_size = 5; + break; + default: + max_payload_size = 1; + break; + } + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val &= (~(0x7<<5)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val |= (max_payload_size<<5); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); +} + +void amlogic_set_max_rd_req_size(struct amlogic_pcie *amlogic_pcie, int size) +{ + int max_rd_req_size = 1; + u32 val = 0; + + switch (size) { + case 128: + max_rd_req_size = 0; + break; + case 256: + max_rd_req_size = 1; + break; + case 512: + max_rd_req_size = 2; + break; + case 1024: + max_rd_req_size = 3; + break; + case 2048: + max_rd_req_size = 4; + break; + case 4096: + max_rd_req_size = 5; + break; + default: + max_rd_req_size = 1; + break; + } + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val &= (~(0x7<<12)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val |= (max_rd_req_size<<12); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static void amlogic_pcie_init_dw(struct amlogic_pcie *amlogic_pcie) +{ + u32 val = 0; +#if 0 + if (amlogic_pcie->pcie_num == 1) { + val = readl(amlogic_pcie->reset_base); + val &= ~((0x3<<6) | (0x3<<1)); + writel(val, amlogic_pcie->reset_base); + + mdelay(10); + + val = readl(amlogic_pcie->reset_base); + val |= (0x3<<6) | (0x3<<1); + writel(val, amlogic_pcie->reset_base); + } + mdelay(10); +#endif + + val = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG0); + val |= APP_LTSSM_ENABLE; + amlogic_cfg_writel(amlogic_pcie, val, PCIE_CFG0); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF); + val |= (1<<7); + amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF); + val &= (~(0x3f<<16)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF); + val |= (0x1<<16); + amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF); + val &= (~(0x1f<<8)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF); + val |= (0x1<<8); + amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF); + val |= (1<<17); + amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF); + + amlogic_elb_writel(amlogic_pcie, 0x0, PCIE_BASE_ADDR0); + amlogic_elb_writel(amlogic_pcie, 0x0, PCIE_BASE_ADDR1); +} + +void amlogic_enable_memory_space(struct amlogic_pcie *amlogic_pcie) +{ + u32 val = 0; + + dev_info(amlogic_pcie->pp.dev, "Set the RC Bus Master, Memory Space and I/O Space enables.\n"); + val = amlogic_elb_readl(amlogic_pcie, 0x04); + val = 7; + amlogic_elb_writel(amlogic_pcie, val, 0x04); +} + + +static int amlogic_pcie_establish_link(struct amlogic_pcie *amlogic_pcie) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + + amlogic_pcie_assert_reset(amlogic_pcie); + + amlogic_pcie_init_dw(amlogic_pcie); + amlogic_set_max_payload(amlogic_pcie, 256); + amlogic_set_max_rd_req_size(amlogic_pcie, 256); + + dw_pcie_setup_rc(pp); + amlogic_enable_memory_space(amlogic_pcie); + + /* check if the link is up or not */ + if (!dw_pcie_wait_for_link(pp)) + return 0; + + return -ETIMEDOUT; +} + +static irqreturn_t amlogic_pcie_irq_handler(int irq, void *arg) +{ + return IRQ_HANDLED; +} + +static irqreturn_t amlogic_pcie_msi_irq_handler(int irq, void *arg) +{ + struct amlogic_pcie *amlogic_pcie = arg; + struct pcie_port *pp = &amlogic_pcie->pp; + + return dw_handle_msi_irq(pp); +} + +static void amlogic_pcie_msi_init(struct amlogic_pcie *amlogic_pcie) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + + dw_pcie_msi_init(pp); +} + +static void amlogic_pcie_enable_interrupts(struct amlogic_pcie *amlogic_pcie) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) + amlogic_pcie_msi_init(amlogic_pcie); +} + +static u32 amlogic_pcie_readl_rc(struct pcie_port *pp, u32 reg) +{ + u32 val; + + val = readl(pp->dbi_base + reg); + return val; +} + +static void amlogic_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val) +{ + writel(val, pp->dbi_base + reg); +} + +static int amlogic_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + int ret; + + /* the device class is not reported correctly from the register */ + if (where == PCI_CLASS_REVISION) { + dev_info(pp->dev, "the device class is not reported correctly from the register\n"); + *val = readl(pp->dbi_base + PCI_CLASS_REVISION); + *val &= 0xff; /* keep revision id */ + *val |= PCI_CLASS_BRIDGE_PCI << 16; + return PCIBIOS_SUCCESSFUL; + } + + ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); + return ret; +} + +static int amlogic_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + int ret; + + ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); + return ret; +} + +int amlogic_pcie_link_up(struct pcie_port *pp) +{ + u32 smlh_up = 0; + u32 rdlh_up = 0; + u32 ltssm_up = 0; + u32 speed_okay = 0; + u32 current_data_rate; + int cnt = 0; + struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp); + + while (smlh_up == 0 || rdlh_up == 0 + || ltssm_up == 0 || speed_okay == 0) { + smlh_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12); + smlh_up = (smlh_up >> 6) & 0x1; + + rdlh_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12); + rdlh_up = (rdlh_up >> 16) & 0x1; + ltssm_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12); + ltssm_up = ((ltssm_up >> 10) & 0x1f) == 0x11 ? 1 : 0; + current_data_rate = amlogic_cfg_readl( + amlogic_pcie, PCIE_CFG_STATUS17); + current_data_rate = (current_data_rate >> 7) & 0x1; + + if ((current_data_rate == PCIE_GEN2) || + (current_data_rate == PCIE_GEN1)) + speed_okay = 1; + + if (smlh_up) + dev_dbg(pp->dev, "smlh_link_up is on\n"); + if (rdlh_up) + dev_dbg(pp->dev, "rdlh_link_up is on\n"); + if (ltssm_up) + dev_dbg(pp->dev, "ltssm_up is on\n"); + if (speed_okay) + dev_dbg(pp->dev, "speed_okay\n"); + + cnt++; + + if (cnt >= WAIT_LINKUP_TIMEOUT) { + dev_err(pp->dev, "Error: Wait linkup timeout.\n"); + return 0; + } + + udelay(2); + } + + if (current_data_rate == PCIE_GEN2) + dev_info(pp->dev, "PCIE SPEED IS GEN2\n"); + + return 1; +} + +static void amlogic_pcie_host_init(struct pcie_port *pp) +{ + struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp); + + amlogic_pcie_establish_link(amlogic_pcie); + amlogic_pcie_enable_interrupts(amlogic_pcie); +} + +static struct pcie_host_ops amlogic_pcie_host_ops = { + .readl_rc = amlogic_pcie_readl_rc, + .writel_rc = amlogic_pcie_writel_rc, + .rd_own_conf = amlogic_pcie_rd_own_conf, + .wr_own_conf = amlogic_pcie_wr_own_conf, + .link_up = amlogic_pcie_link_up, + .host_init = amlogic_pcie_host_init, +}; + +static int __init amlogic_add_pcie_port(struct amlogic_pcie *amlogic_pcie, + struct platform_device *pdev) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + struct device *dev = pp->dev; + int ret; + + pp->irq = platform_get_irq(pdev, 1); + if (!pp->irq) { + dev_err(dev, "failed to get irq\n"); + return -ENODEV; + } + ret = devm_request_irq(dev, pp->irq, amlogic_pcie_irq_handler, + IRQF_SHARED, "amlogic-pcie", amlogic_pcie); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (!pp->msi_irq) { + dev_err(dev, "failed to get msi irq\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, pp->msi_irq, + amlogic_pcie_msi_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + "amlogic-pcie", amlogic_pcie); + if (ret) { + dev_err(dev, "failed to request msi irq\n"); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &amlogic_pcie_host_ops; + pp->dbi_base = amlogic_pcie->elbi_base; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init amlogic_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct amlogic_pcie *amlogic_pcie; + struct pcie_port *pp; + struct device_node *np = dev->of_node; + struct resource *elbi_base; + struct resource *phy_base; + struct resource *cfg_base; + struct resource *reset_base; + int ret; + int pcie_num = 0; + int board_type = 0; + unsigned long rate = 100000000; + int err; + + dev_info(&pdev->dev, "amlogic_pcie_probe!\n"); + + amlogic_pcie = devm_kzalloc(dev, sizeof(*amlogic_pcie), GFP_KERNEL); + if (!amlogic_pcie) + return -ENOMEM; + + pp = &amlogic_pcie->pp; + pp->dev = dev; + + ret = of_property_read_u32(np, "pcie-num", &pcie_num); + if (ret) + pp->lanes = 0; + + amlogic_pcie->pcie_num = pcie_num; + + if (amlogic_pcie->pcie_num == 1) + g_amlogic_pcie = amlogic_pcie; + + ret = of_property_read_u32(np, "board-type", &board_type); + amlogic_pcie->board_type = board_type; + + amlogic_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + + if (amlogic_pcie->pcie_num == 1) + amlogic_pcie->clk = devm_clk_get(dev, "pcie_a"); + else + amlogic_pcie->clk = devm_clk_get(dev, "pcie_b"); + + if (IS_ERR(amlogic_pcie->clk)) { + dev_err(dev, "Failed to get pcie rc clock\n"); + return PTR_ERR(amlogic_pcie->clk); + } + + ret = clk_prepare_enable(amlogic_pcie->clk); + if (ret) + return ret; + + amlogic_pcie->bus_clk = devm_clk_get(dev, "pcie_refpll"); + if (IS_ERR(amlogic_pcie->bus_clk)) { + dev_err(dev, "Failed to get pcie bus clock\n"); + ret = PTR_ERR(amlogic_pcie->bus_clk); + goto fail_clk; + } + + err = clk_set_rate(amlogic_pcie->bus_clk, rate); + if (err) + goto fail_clk; + + if (clk_get_rate(amlogic_pcie->bus_clk) == rate) + goto fail_clk; + + ret = clk_prepare_enable(amlogic_pcie->bus_clk); + if (ret) + goto fail_clk; + + elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); + amlogic_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base); + if (IS_ERR(amlogic_pcie->elbi_base)) { + ret = PTR_ERR(amlogic_pcie->elbi_base); + goto fail_bus_clk; + } + + if (amlogic_pcie->pcie_num == 1) { + phy_base = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "phy"); + amlogic_pcie->phy_base = devm_ioremap_resource(dev, phy_base); + if (IS_ERR(amlogic_pcie->phy_base)) { + ret = PTR_ERR(amlogic_pcie->phy_base); + goto fail_bus_clk; + } + } else { + amlogic_pcie->phy_base = g_amlogic_pcie->phy_base; + } + + cfg_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + amlogic_pcie->cfg_base = devm_ioremap_resource(dev, cfg_base); + if (IS_ERR(amlogic_pcie->cfg_base)) { + ret = PTR_ERR(amlogic_pcie->cfg_base); + goto fail_bus_clk; + } + + if (amlogic_pcie->pcie_num == 1) { + reset_base = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "reset"); + amlogic_pcie->reset_base = devm_ioremap_resource( + dev, reset_base); + if (IS_ERR(amlogic_pcie->reset_base)) { + ret = PTR_ERR(amlogic_pcie->reset_base); + goto fail_bus_clk; + } + } else { + amlogic_pcie->reset_base = g_amlogic_pcie->reset_base; + } + + ret = amlogic_add_pcie_port(amlogic_pcie, pdev); + if (ret < 0) + goto fail_bus_clk; + + platform_set_drvdata(pdev, amlogic_pcie); + return 0; + +fail_bus_clk: + clk_disable_unprepare(amlogic_pcie->bus_clk); +fail_clk: + clk_disable_unprepare(amlogic_pcie->clk); + + return ret; +} + +static int __exit amlogic_pcie_remove(struct platform_device *pdev) +{ + struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev); + + clk_disable_unprepare(amlogic_pcie->bus_clk); + clk_disable_unprepare(amlogic_pcie->clk); + + return 0; +} + +static const struct of_device_id amlogic_pcie_of_match[] = { + { .compatible = "amlogic, amlogic-pcie", }, + {}, +}; + +static struct platform_driver amlogic_pcie_driver = { + .remove = __exit_p(amlogic_pcie_remove), + .driver = { + .name = "amlogic-pcie", + .of_match_table = amlogic_pcie_of_match, + }, +}; + +/* Exynos PCIe driver does not allow module unload */ +static int __init amlogic_pcie_init(void) +{ + return platform_driver_probe(&amlogic_pcie_driver, amlogic_pcie_probe); +} +subsys_initcall(amlogic_pcie_init); diff --git a/drivers/amlogic/pci/pcie-amlogic.h b/drivers/amlogic/pci/pcie-amlogic.h new file mode 100644 index 000000000000..7c6e885f0f70 --- /dev/null +++ b/drivers/amlogic/pci/pcie-amlogic.h @@ -0,0 +1,48 @@ +/* + * drivers/amlogic/pci/pcie-amlogic.h + * + * Copyright (C) 2017 Amlogic, 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. + * + */ + +#ifndef __AMLOGIC_PCI_H__ +#define __AMLOGIC_PCI_H__ + +/* PCIe Port Logic registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PORT_LINK_CTRL_OFF (PLR_OFFSET + 0x10) +#define PCIE_GEN2_CTRL_OFF (PLR_OFFSET + 0x10c) + +#define TYPE1_HDR_OFFSET 0X0 +#define PCIE_BASE_ADDR0 (TYPE1_HDR_OFFSET + 0X10) +#define PCIE_BASE_ADDR1 (TYPE1_HDR_OFFSET + 0x14) + +#define PCIE_CFG0 0x0 + +#define APP_LTSSM_ENABLE (1<<7) +#define PCIE_CFG_STATUS12 0x30 +#define PCIE_CFG_STATUS17 0x44 + +#define WAIT_LINKUP_TIMEOUT 5000 + +enum pcie_data_rate { + PCIE_GEN1, + PCIE_GEN2, + PCIE_GEN3, + PCIE_GEN4 +}; + +#define PCIE_CAP_OFFSET 0x70 +#define PCIE_DEV_CTRL_DEV_STUS (PCIE_CAP_OFFSET + 0x08) + +#endif