kernel_samsung_a53x/drivers/pci/controller/dwc/pcie-exynos-dbg.c
2024-06-15 16:02:09 -03:00

396 lines
10 KiB
C
Executable file

/*
* PCIe host controller driver for Samsung EXYNOS SoCs
*
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/resource.h>
#include <linux/signal.h>
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/random.h>
#include <linux/gpio.h>
#include "pcie-designware.h"
#include "pcie-exynos-common.h"
#include "pcie-exynos-dbg.h"
#include "pcie-exynos-rc.h"
static int chk_pcie_dislink(struct exynos_pcie *exynos_pcie)
{
int test_result = 0;
struct exynos_pcie_ops *pcie_ops = &exynos_pcie->exynos_pcie_ops;
u32 linkup_offset = exynos_pcie->linkup_offset;
u32 val;
if (!pcie_ops->poweroff) {
pr_err("Can't find PCIe poweroff function\n");
return -1;
}
pcie_ops->poweroff(exynos_pcie->ch_num);
val = exynos_elbi_read(exynos_pcie,
linkup_offset) & 0x1f;
if (val == 0x15 || val == 0x0) {
pr_info("PCIe link Down test Success.\n");
} else {
pr_info("PCIe Link Down test Fail...\n");
test_result = -1;
}
return test_result;
}
static int chk_link_recovery(struct exynos_pcie *exynos_pcie)
{
int test_result = 0;
struct exynos_pcie_ops *pcie_ops = &exynos_pcie->exynos_pcie_ops;
u32 linkup_offset = exynos_pcie->linkup_offset;
u32 val;
if (!pcie_ops->poweroff) {
pr_err("Can't find PCIe poweroff function\n");
return -1;
}
if (!pcie_ops->poweron) {
pr_err("Can't find PCIe poweron function\n");
return -1;
}
exynos_elbi_write(exynos_pcie, 0x1, exynos_pcie->app_req_exit_l1);
val = exynos_elbi_read(exynos_pcie, exynos_pcie->app_req_exit_l1_mode);
val &= ~APP_REQ_EXIT_L1_MODE;
exynos_elbi_write(exynos_pcie, val, exynos_pcie->app_req_exit_l1_mode);
pr_info("%s: Before set perst, gpio val = %d\n",
__func__, gpio_get_value(exynos_pcie->perst_gpio));
gpio_set_value(exynos_pcie->perst_gpio, 0);
msleep(5000);
pr_info("%s: After set perst, gpio val = %d\n",
__func__, gpio_get_value(exynos_pcie->perst_gpio));
val = exynos_elbi_read(exynos_pcie, exynos_pcie->app_req_exit_l1_mode);
val |= APP_REQ_EXIT_L1_MODE;
exynos_elbi_write(exynos_pcie, val, exynos_pcie->app_req_exit_l1_mode);
exynos_elbi_write(exynos_pcie, 0x0, exynos_pcie->app_req_exit_l1);
msleep(5000);
val = exynos_elbi_read(exynos_pcie,
linkup_offset) & 0x1f;
if (val >= 0x0d && val <= 0x14) {
pr_info("PCIe link Recovery test Success.\n");
} else {
/* If recovery callback is defined, pcie poweron
* function will not be called.
*/
pcie_ops->poweroff(exynos_pcie->ch_num);
pcie_ops->poweron(exynos_pcie->ch_num);
val = exynos_elbi_read(exynos_pcie,
linkup_offset) & 0x1f;
if (val >= 0x0d && val <= 0x14) {
pr_info("PCIe link Recovery test Success.\n");
} else {
pr_info("PCIe Link Recovery test Fail...\n");
test_result = -1;
}
}
return test_result;
}
static int chk_epmem_access(struct exynos_pcie *exynos_pcie)
{
u32 val;
int test_result = 0;
struct dw_pcie *pci = exynos_pcie->pci;
struct pcie_port *pp = &pci->pp;
struct exynos_pcie_ops *pcie_ops
= &exynos_pcie->exynos_pcie_ops;
struct pci_bus *ep_pci_bus;
void __iomem *reg_addr;
struct resource_entry *tmp = NULL, *entry = NULL;
if (!pcie_ops->rd_other_conf) {
pr_err("Can't find PCIe read other configuration function\n");
return -1;
}
if (!pcie_ops->wr_other_conf) {
pr_err("Can't find PCIe write other configuration function\n");
return -1;
}
/* Get last memory resource entry */
resource_list_for_each_entry(tmp, &pp->bridge->windows)
if (resource_type(tmp->res) == IORESOURCE_MEM)
entry = tmp;
ep_pci_bus = pci_find_bus(exynos_pcie->pci_dev->bus->domain_nr, 1);
if (ep_pci_bus == NULL) {
pr_err("Can't find PCIe ep_pci_bus structure\n");
return -1;
}
pcie_ops->wr_other_conf(pp, ep_pci_bus, 0, PCI_BASE_ADDRESS_0,
4, lower_32_bits(entry->res->start));
pcie_ops->rd_other_conf(pp, ep_pci_bus, 0, PCI_BASE_ADDRESS_0,
4, &val);
pr_info("Set BAR0 : 0x%x\n", val);
reg_addr = ioremap(entry->res->start, SZ_4K);
val = readl(reg_addr);
iounmap(reg_addr);
if (val != 0xffffffff) {
pr_info("PCIe EP Outbound mem access Success.\n");
} else {
pr_info("PCIe EP Outbound mem access Fail...\n");
test_result = -1;
}
return test_result;
}
static int chk_epconf_access(struct exynos_pcie *exynos_pcie)
{
u32 val;
int test_result = 0;
struct dw_pcie *pci = exynos_pcie->pci;
struct pcie_port *pp = &pci->pp;
struct pci_bus *ep_pci_bus;
struct exynos_pcie_ops *pcie_ops
= &exynos_pcie->exynos_pcie_ops;
if (!pcie_ops->rd_other_conf) {
pr_err("Can't find PCIe read other configuration function\n");
return -1;
}
if (!pcie_ops->wr_other_conf) {
pr_err("Can't find PCIe write other configuration function\n");
return -1;
}
ep_pci_bus = pci_find_bus(exynos_pcie->pci_dev->bus->domain_nr, 1);
pcie_ops->rd_other_conf(pp, ep_pci_bus, 0, 0x0, 4, &val);
pr_info("PCIe EP Vendor ID/Device ID = 0x%x\n", val);
pcie_ops->wr_other_conf(pp, ep_pci_bus,
0, PCI_COMMAND, 4, 0x146);
pcie_ops->rd_other_conf(pp, ep_pci_bus,
0, PCI_COMMAND, 4, &val);
if ((val & 0xfff) == 0x146) {
pr_info("PCIe EP conf access Success.\n");
} else {
pr_info("PCIe EP conf access Fail...\n");
test_result = -1;
}
return test_result;
}
static int chk_dbi_access(struct exynos_pcie *exynos_pcie)
{
u32 val;
int test_result = 0;
struct dw_pcie *pci = exynos_pcie->pci;
struct pcie_port *pp = &pci->pp;
struct exynos_pcie_ops *pcie_ops
= &exynos_pcie->exynos_pcie_ops;
if (!pcie_ops->rd_own_conf) {
pr_err("Can't find PCIe read own configuration function\n");
return -1;
}
if (!pcie_ops->wr_own_conf) {
pr_err("Can't find PCIe write own configuration function\n");
return -1;
}
pcie_ops->wr_own_conf(pp, PCI_COMMAND, 4, 0x140);
pcie_ops->rd_own_conf(pp, PCI_COMMAND, 4, &val);
if ((val & 0xfff) == 0x140) {
pr_info("PCIe DBI access Success.\n");
} else {
pr_info("PCIe DBI access Fail...\n");
test_result = -1;
}
return test_result;
}
static int chk_pcie_link(struct exynos_pcie *exynos_pcie)
{
int test_result = 0;
struct exynos_pcie_ops *pcie_ops = &exynos_pcie->exynos_pcie_ops;
u32 linkup_offset = exynos_pcie->linkup_offset;
u32 val;
if (!pcie_ops->poweron) {
pr_err("Can't find PCIe poweron function\n");
return -1;
}
pcie_ops->poweron(exynos_pcie->ch_num);
val = exynos_elbi_read(exynos_pcie,
linkup_offset) & 0x1f;
if (val >= 0x0d && val <= 0x14) {
pr_info("PCIe link test Success.\n");
} else {
pr_info("PCIe Link test Fail...\n");
test_result = -1;
}
return test_result;
}
int exynos_pcie_dbg_unit_test(struct device *dev, struct exynos_pcie *exynos_pcie)
{
int ret = 0;
if (exynos_pcie->ssd_gpio < 0) {
dev_warn(dev, "can't find ssd pin info. Need to check EP device power pin\n");
} else {
gpio_set_value(exynos_pcie->ssd_gpio, 1);
mdelay(100);
}
if (exynos_pcie->wlan_gpio < 0) {
dev_warn(dev, "can't find wlan pin info. Need to check EP device power pin\n");
} else {
gpio_direction_output(exynos_pcie->wlan_gpio, 0);
gpio_set_value(exynos_pcie->wlan_gpio, 1);
mdelay(100);
}
dev_info(dev, "1. Test PCIe LINK... \n");
/* Test PCIe Link */
if (chk_pcie_link(exynos_pcie)) {
dev_info(dev, "PCIe UNIT test FAIL[1/6]!!!\n");
ret = -1;
goto done;
}
dev_info(dev, "2. Test DBI access... \n");
/* Test PCIe DBI access */
if (chk_dbi_access(exynos_pcie)) {
dev_info(dev, "PCIe UNIT test FAIL[2/6]!!!\n");
ret = -2;
goto done;
}
dev_info(dev, "3. Test EP configuration access... \n");
/* Test EP configuration access */
if (chk_epconf_access(exynos_pcie)) {
dev_info(dev, "PCIe UNIT test FAIL[3/6]!!!\n");
ret = -3;
goto done;
}
dev_info(dev, "4. Test EP Outbound memory region... \n");
/* Test EP Outbound memory region */
if (chk_epmem_access(exynos_pcie)) {
dev_info(dev, "PCIe UNIT test FAIL[4/6]!!!\n");
ret = -4;
goto done;
}
dev_info(dev, "5. Test PCIe Link recovery... \n");
/* PCIe Link recovery test */
if (chk_link_recovery(exynos_pcie)) {
dev_info(dev, "PCIe UNIT test FAIL[5/6]!!!\n");
ret = -5;
goto done;
}
dev_info(dev, "6. Test PCIe Dislink... \n");
/* PCIe DisLink Test */
if (chk_pcie_dislink(exynos_pcie)) {
dev_info(dev, "PCIe UNIT test FAIL[6/6]!!!\n");
ret = -6;
goto done;
}
done:
return ret;
}
EXPORT_SYMBOL(exynos_pcie_dbg_unit_test);
int exynos_pcie_dbg_link_test(struct device *dev,
struct exynos_pcie *exynos_pcie, int enable)
{
struct exynos_pcie_ops *pcie_ops = &exynos_pcie->exynos_pcie_ops;
int ret;
dev_info(dev, "TEST PCIe %sLink Test \n", enable ? "" : "Dis");
if (enable) {
if (!pcie_ops->poweron) {
pr_err("Can't find PCIe poweron function\n");
return -1;
}
if (exynos_pcie->ssd_gpio < 0) {
dev_warn(dev, "can't find ssd pin info. Need to check EP device power pin\n");
} else {
gpio_set_value(exynos_pcie->ssd_gpio, 1);
mdelay(100);
}
if (exynos_pcie->wlan_gpio < 0) {
dev_warn(dev, "can't find wlan pin info. Need to check EP device power pin\n");
} else {
dev_err(dev, "## make gpio direction to output\n");
gpio_direction_output(exynos_pcie->wlan_gpio, 0);
dev_err(dev, "## make gpio set high\n");
gpio_set_value(exynos_pcie->wlan_gpio, 1);
mdelay(100);
}
mdelay(100);
ret = pcie_ops->poweron(exynos_pcie->ch_num);
} else {
if (!pcie_ops->poweroff) {
pr_err("Can't find PCIe poweroff function\n");
return -1;
}
pcie_ops->poweroff(exynos_pcie->ch_num);
if (exynos_pcie->ssd_gpio < 0) {
dev_warn(dev, "can't find ssd pin info. Need to check EP device power pin\n");
} else {
gpio_set_value(exynos_pcie->ssd_gpio, 0);
}
if (exynos_pcie->wlan_gpio < 0) {
dev_warn(dev, "can't find wlan pin info. Need to check EP device power pin\n");
} else {
gpio_set_value(exynos_pcie->wlan_gpio, 0);
}
ret = 0;
}
return ret;
}
EXPORT_SYMBOL(exynos_pcie_dbg_link_test);
MODULE_AUTHOR("Kisang Lee <kisang80.lee@samsung.com>");
MODULE_AUTHOR("Kwangho Kim <kwangho2.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung PCIe host controller driver");
MODULE_LICENSE("GPL v2");