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 <yue.wang@amlogic.com>
This commit is contained in:
Yue Wang
2017-05-12 09:55:03 +08:00
parent 26efd0ca10
commit a417b7a6e4
12 changed files with 915 additions and 0 deletions

View File

@@ -13901,3 +13901,10 @@ M: Yun Cai <yun.cai@amlogic.com>
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 <yue.wang@amlogic.com>
F: drivers/amlogic/pci/pcie-amlogic.c
F: drivers/amlogic/pci/pcie-amlogic.h
F: drivers/amlogic/pci/Kconfig
F: drivers/amlogic/pci/Makefile

View File

@@ -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 / */

View File

@@ -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 / */

View File

@@ -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 / */

View File

@@ -20,6 +20,7 @@
#include <dt-bindings/gpio/mesonaxg-gpio.h>
#include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/pwm/meson.h>
#include <dt-bindings/gpio/gpio.h>
/ {
cpus:cpus {

View File

@@ -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

View File

@@ -100,5 +100,7 @@ source "drivers/amlogic/dvb/Kconfig"
source "drivers/amlogic/power/Kconfig"
source "drivers/amlogic/pci/Kconfig"
endmenu
endif

View File

@@ -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/

View File

@@ -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

View File

@@ -0,0 +1,5 @@
#
# Amlogic PCIE specific Makefile
#
obj-$(CONFIG_AMLOGIC_PCIE) += pcie-amlogic.o

View File

@@ -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 <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
#include <linux/signal.h>
#include <linux/types.h>
#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);

View File

@@ -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