blob: 499c9f259456b520f8d3d14ae283b4a215e38555 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
* Author: Jianjun Wang <jianjun.wang@mediatek.com>
*
*/
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/pm_domain.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/gpio.h>
#define PCIE_PHY_0_NUM 0
#define PCIE_PHY_1_NUM 1
#define PCIE_PHY_2_NUM 2
#define PCIE_PHY_3_NUM 3
#define PCIE_PHY_0_EFUSE_CNT 5
#define PCIE_PHY_CKM_REG04 0x04
#define PCIE_EFSUE_P1_INDEX 0x108
#define PCIE_EFSUE_P2_INDEX 0x10C
#define PCIE_EFSUE_P3_INDEX 0x110
#define CKM_PT0_CKTX_IMPSEL(val) ((val) & GENMASK(3, 0))
#define CKM_PT0_CKTX_R50_EN BIT(5)
#define CKM_PT0_CKTX_SLEW(val) ((val << 8) & GENMASK(9, 8))
#define CKM_PT0_CKTX_OFFSET(val) ((val << 10) & GENMASK(11, 10))
#define CKM_PT0_CKTX_AMP(val) ((val << 12) & GENMASK(15, 12))
#define PEXTP_GLB_00_RG 0x9000
#define PEXTP_04_RG 0xA004
#define PEXTP_3C_RG 0xA03C
#define PEXTP_104_RG 0xA104
#define PEXTP_13C_RG 0xA13c
#define EFUSE_FIELD_SIZE 4
#define EFUSE_SHIFT(val, right, left, left_phy) \
((((val) & GENMASK(right, left)) >> (left)) << (left_phy))
struct mtk_pcie_phy {
struct device *dev;
struct phy *phy;
void __iomem *sif_base;
void __iomem *ckm_base;
struct clk *pipe_clk; /* pipe clock to mac */
enum phy_mode mode;
};
static const unsigned int efuse_field_offset[PCIE_PHY_0_EFUSE_CNT] = {
0x100, 0x104, 0x1B4, 0x1BC, 0x1C0
};
/*
* MT6890 RC portA is unlike the other ports, needs to get
* impedance select values from the efuse's multiple registers.
*/
static int pcie_fetch_efuse_scatter(struct mtk_pcie_phy *pcie_phy)
{
struct nvmem_device *nvmem_dev;
u32 efuse_addr[PCIE_PHY_0_EFUSE_CNT] = {0}, val;
int ret;
int i;
nvmem_dev = nvmem_device_get(pcie_phy->dev, "mtk_efuse");
if (IS_ERR(nvmem_dev)) {
if (PTR_ERR(nvmem_dev) == -EPROBE_DEFER)
return PTR_ERR(nvmem_dev);
return -EINVAL;
}
for (i = 0; i < PCIE_PHY_0_EFUSE_CNT; i++) {
ret = nvmem_device_read(nvmem_dev, efuse_field_offset[i],
EFUSE_FIELD_SIZE, &efuse_addr[i]);
if (ret != EFUSE_FIELD_SIZE) {
dev_warn(pcie_phy->dev, "pcie efuse val is error\n");
return -EINVAL;
}
}
if (efuse_addr[0] == 0) {
dev_err(pcie_phy->dev, "Failed to read efuse value\n");
return -EINVAL;
}
/* PEXTP_GLB_00_RG[28:24] Internal RG Selection of TX Bias Current */
val = readl(pcie_phy->sif_base + PEXTP_GLB_00_RG);
val = (val & ~GENMASK(28, 24)) | EFUSE_SHIFT(efuse_addr[0], 27, 23, 24);
writel(val, pcie_phy->sif_base + PEXTP_GLB_00_RG);
/* PCIE_PHY_CKM_REG04[3:0] Internal R50 impedance select */
val = readl(pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr[1], 8, 5, 0);
writel(val, pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
/* PEXTP_04_RG[5:2] LN0 TX PMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_04_RG);
val = (val & ~GENMASK(5, 2)) | EFUSE_SHIFT(efuse_addr[2], 4, 1, 2);
writel(val, pcie_phy->sif_base + PEXTP_04_RG);
/* PEXTP_104_RG[5:2] LN1 TX PMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_104_RG);
val = (val & ~GENMASK(5, 2)) | EFUSE_SHIFT(efuse_addr[2], 8, 5, 2);
writel(val, pcie_phy->sif_base + PEXTP_104_RG);
/* PEXTP_04_RG[11:8] LN0 TX NMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_04_RG);
val = (val & ~GENMASK(11, 8)) | EFUSE_SHIFT(efuse_addr[2], 20, 17, 8);
writel(val, pcie_phy->sif_base + PEXTP_04_RG);
/* PEXTP_104_RG[11:8] LN1 TX NMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_104_RG);
val = (val & ~GENMASK(11, 8)) | EFUSE_SHIFT(efuse_addr[2], 24, 21, 8);
writel(val, pcie_phy->sif_base + PEXTP_104_RG);
/* PEXTP_3C_RG[3:0] LN0 RX impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_3C_RG);
val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr[3], 31, 28, 0);
writel(val, pcie_phy->sif_base + PEXTP_3C_RG);
/* PEXTP_13C_RG[3:0] LN1 RX impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_13C_RG);
val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr[4], 3, 0, 0);
writel(val, pcie_phy->sif_base + PEXTP_13C_RG);
nvmem_device_put(nvmem_dev);
return 0;
}
static int pcie_fetch_efuse_unify(struct mtk_pcie_phy *pcie_phy)
{
struct nvmem_device *nvmem_dev;
u32 efuse_addr = 0, val;
unsigned int efuse_index_offset = 0;
int ret;
switch (pcie_phy->phy->id) {
case PCIE_PHY_1_NUM:
efuse_index_offset = PCIE_EFSUE_P1_INDEX;
break;
case PCIE_PHY_2_NUM:
efuse_index_offset = PCIE_EFSUE_P2_INDEX;
break;
case PCIE_PHY_3_NUM:
efuse_index_offset = PCIE_EFSUE_P3_INDEX;
break;
default:
dev_info(pcie_phy->dev, "pcie phy num for efuse is error\n");
}
nvmem_dev = nvmem_device_get(pcie_phy->dev, "mtk_efuse");
if (IS_ERR(nvmem_dev)) {
if (PTR_ERR(nvmem_dev) == -EPROBE_DEFER)
return PTR_ERR(nvmem_dev);
return -EINVAL;
}
ret = nvmem_device_read(nvmem_dev, efuse_index_offset,
EFUSE_FIELD_SIZE, &efuse_addr);
if (ret != EFUSE_FIELD_SIZE) {
dev_warn(pcie_phy->dev, "pcie efuse val is error\n");
return -EINVAL;
}
if (efuse_addr == 0) {
dev_err(pcie_phy->dev, "Failed to read efuse value\n");
return -EINVAL;
}
/* PEXTP_3C_RG[3:0] LN0 RX impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_3C_RG);
val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr, 3, 0, 0);
writel(val, pcie_phy->sif_base + PEXTP_3C_RG);
/* PEXTP_04_RG[5:2] LN0 TX PMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_04_RG);
val = (val & ~GENMASK(5, 2)) | EFUSE_SHIFT(efuse_addr, 7, 4, 2);
writel(val, pcie_phy->sif_base + PEXTP_04_RG);
/* PEXTP_04_RG[11:8] LN0 TX NMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_04_RG);
val = (val & ~GENMASK(11, 8)) | EFUSE_SHIFT(efuse_addr, 11, 8, 8);
writel(val, pcie_phy->sif_base + PEXTP_04_RG);
/* PEXTP_GLB_00_RG[28:24] Internal RG Selection of TX Bias Current */
val = readl(pcie_phy->sif_base + PEXTP_GLB_00_RG);
val = (val & ~GENMASK(28, 24)) | EFUSE_SHIFT(efuse_addr, 16, 12, 24);
writel(val, pcie_phy->sif_base + PEXTP_GLB_00_RG);
/* portB is two lanes */
if (pcie_phy->phy->id == 1) {
/* PEXTP_13C_RG[3:0] LN1 RX impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_13C_RG);
val = (val & ~GENMASK(3, 0)) |
EFUSE_SHIFT(efuse_addr, 20, 17, 0);
writel(val, pcie_phy->sif_base + PEXTP_13C_RG);
/* PEXTP_104_RG[5:2] LN1 TX PMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_104_RG);
val = (val & ~GENMASK(5, 2)) |
EFUSE_SHIFT(efuse_addr, 24, 21, 2);
writel(val, pcie_phy->sif_base + PEXTP_104_RG);
/* PEXTP_104_RG[11:8] LN1 TX NMOS impedance selection */
val = readl(pcie_phy->sif_base + PEXTP_104_RG);
val = (val & ~GENMASK(11, 8)) |
EFUSE_SHIFT(efuse_addr, 28, 25, 8);
writel(val, pcie_phy->sif_base + PEXTP_104_RG);
}
nvmem_device_put(nvmem_dev);
return 0;
}
static int mtk_pcie_phy_init(struct phy *phy)
{
struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
struct device *dev = pcie_phy->dev;
struct generic_pm_domain *pcie_pd;
int err;
if (pcie_phy->mode == PHY_MODE_PCIE_EP) {
dev_info(pcie_phy->dev, "set phy to EP mode\n");
if (dev->pm_domain) {
pcie_pd = pd_to_genpd(dev->pm_domain);
pcie_pd->flags |= GENPD_FLAG_ALWAYS_ON;
}
}
if (pcie_phy->mode == PHY_MODE_PCIE_RC) {
if (pcie_phy->phy->id == PCIE_PHY_0_NUM) {
err = pcie_fetch_efuse_scatter(pcie_phy);
if (err)
return -EINVAL;
}
if (pcie_phy->phy->id > PCIE_PHY_0_NUM) {
err = pcie_fetch_efuse_unify(pcie_phy);
if (err)
return -EINVAL;
}
}
return 0;
}
static int mtk_pcie_phy_power_on(struct phy *phy)
{
struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
int val, ret;
pm_runtime_get_sync(pcie_phy->dev);
if (pcie_phy->mode == PHY_MODE_PCIE_RC) {
val = readl(pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
val &= ~GENMASK(11, 10);
val |= CKM_PT0_CKTX_OFFSET(0x2);
writel(val, pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
}
/* provide pipe clock to pcie mac */
if (pcie_phy->pipe_clk) {
ret = clk_prepare_enable(pcie_phy->pipe_clk);
if (ret) {
dev_err(pcie_phy->dev, "failed to enable pipe_clk\n");
return ret;
}
}
return 0;
}
static int mtk_pcie_phy_power_off(struct phy *phy)
{
struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
if (pcie_phy->pipe_clk)
clk_disable_unprepare(pcie_phy->pipe_clk);
pm_runtime_put_sync(pcie_phy->dev);
return 0;
}
static int mtk_pcie_phy_exit(struct phy *phy)
{
return 0;
}
static int mtk_pcie_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
pcie_phy->mode = mode;
return 0;
}
static const struct phy_ops mtk_pcie_phy_ops = {
.init = mtk_pcie_phy_init,
.exit = mtk_pcie_phy_exit,
.power_on = mtk_pcie_phy_power_on,
.power_off = mtk_pcie_phy_power_off,
.set_mode = mtk_pcie_phy_set_mode,
.owner = THIS_MODULE,
};
static struct phy *mtk_pcie_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct mtk_pcie_phy *pcie_phy = dev_get_drvdata(dev);
if (!pcie_phy)
return NULL;
return pcie_phy->phy;
}
//tianyan@2021.10.21 modify for pcie loopback test start
static void pcie_loopback_save_reg(void __iomem *phy_base,int *reg,int *reg_value,int len,bool flag)
{
int i=0;
for(i=0; i<len; i++){
if(flag)
*(reg_value+i) = readl(phy_base + *reg++);
else
writel(*(reg_value+i),phy_base + *reg++);
}
}
/* Loopback test for PCIe port
* Port 0 -> 2 lane
*/
static int pcie_loopback_test(void __iomem *phy_base)
{
int val = 0, ret = 0;
int err_count = 0;
int reg[6]={0x28,0x70,0x4010,0x4110,0x501c,0x511c};
int reg_value[6]={0};
pr_info("pcie loopback test start\n");
if (!phy_base) {
pr_info("phy_base is not initialed!\n");
return -1;
}
pcie_loopback_save_reg(phy_base,reg,reg_value,6,true);
/* L1ss = enable */
val = readl(phy_base + 0x28);
val |= (0x01 << 5);
writel(val, phy_base + 0x28);
val = readl(phy_base + 0x28);
val |= (0x01 << 4);
writel(val, phy_base + 0x28);
val = readl(phy_base + 0x28);
val &= (~0x200);
writel(val, phy_base + 0x28);
val = readl(phy_base + 0x28);
val |= (0x01 << 8);
writel(val, phy_base + 0x28);
val = readl(phy_base + 0x28);
val &= (~0x800);
writel(val, phy_base + 0x28);
val = readl(phy_base + 0x28);
val |= (0x01 << 10);
writel(val, phy_base + 0x28);
/* Set Rate=Gen1 */
usleep_range(1, 2);
val = readl(phy_base + 0x70);
val |= (0x01);
writel(val, phy_base + 0x70);
val = readl(phy_base + 0x70);
val &= (~0x30000);
writel(val, phy_base + 0x70);
val = readl(phy_base + 0x70);
val |= (0x01 << 4);
writel(val, phy_base + 0x70);
usleep_range(1, 2);
val = readl(phy_base + 0x70);
val &= (~0x10);
writel(val, phy_base + 0x70);
/* Force PIPE (P0) */
val = readl(phy_base + 0x70);
val |= (0x01);
writel(val, phy_base + 0x70);
val = readl(phy_base + 0x70);
val &= (~0xc00);
writel(val, phy_base + 0x70);
val = readl(phy_base + 0x70);
val &= (~0x3000);
writel(val, phy_base + 0x70);
val = readl(phy_base + 0x70);
val |= (0x01 << 8);
writel(val, phy_base + 0x70);
val = readl(phy_base + 0x70);
val |= (0x01 << 4);
writel(val, phy_base + 0x70);
usleep_range(1, 2);
val = readl(phy_base + 0x70);
val &= (~0x10);
writel(val, phy_base + 0x70);
/* Set TX output Pattern */
usleep_range(1, 2);
/* Lane 0 */
val = readl(phy_base + 0x4010);
val |= (0x0d);
writel(val, phy_base + 0x4010);
/* Lane 1 */
val = readl(phy_base + 0x4110);
val |= (0x0d);
writel(val, phy_base + 0x4110);
/* Set TX PTG Enable */
val = readl(phy_base + 0x28);
val |= (0x01 << 30);
writel(val, phy_base + 0x28);
/* Set RX Pattern Checker (Type & Enable) */
/* Lane 0 */
val = readl(phy_base + 0x501c);
val |= (0x01 << 1);
writel(val, phy_base + 0x501c);
val = readl(phy_base + 0x501c);
val |= (0x0d << 4);
writel(val, phy_base + 0x501c);
/* Lane 1 */
val = readl(phy_base + 0x511c);
val |= (0x01 << 1);
writel(val, phy_base + 0x511c);
val = readl(phy_base + 0x511c);
val |= (0x0d << 4);
writel(val, phy_base + 0x511c);
/* toggle ptc_en for status counter clear */
/* Lane 0 */
val = readl(phy_base + 0x501c);
val &= (~0x2);
writel(val, phy_base + 0x501c);
val = readl(phy_base + 0x501c);
val |= (0x01 << 1);
writel(val, phy_base + 0x501c);
/* Lane 1 */
val = readl(phy_base + 0x511c);
val &= (~0x2);
writel(val, phy_base + 0x511c);
val = readl(phy_base + 0x511c);
val |= (0x01 << 1);
writel(val, phy_base + 0x511c);
/* Check status */
msleep(50);
val = readl(phy_base + 0x50c8);
if ((val & 0x3) != 0x3) {
err_count = val >> 12;
pr_info("PCIe test failed: %#x!\n", val);
pr_info("lane0 error count: %d\n", err_count);
ret = -1;
}
val = readl(phy_base + 0x51c8);
if ((val & 0x3) != 0x3) {
err_count = val >> 12;
pr_info("PCIe test failed: %#x!\n", val);
pr_info("lane1 error count: %d\n", err_count);
ret = -1;
}
pcie_loopback_save_reg(phy_base,reg,reg_value,6,false);
return ret;
}
static bool pcie_loopback_test_res=false;
static int pcie_loopback_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t pcie_loopback_debugfs_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
bool *result = file->private_data;
const int size = 128;
char *buffer = NULL; /* for reduce kernel stack */
int n = 0;
int ret = 0;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer || !buf)
return -ENOMEM;
if (!(*result)) {
pr_info("pcie loopback test failed!\n");
n += scnprintf(buffer + n, size - n, "pcie loopback test failed!\n");
}
else{
pr_info("pcie loopback test success!\n");
n += scnprintf(buffer + n, size - n, "pcie loopback test success!\n");
}
ret = simple_read_from_buffer(buf, count, pos, buffer, n);
kfree(buffer);
return ret;
}
static const struct file_operations pcie_loopback_debugfs_ops = {
.open = pcie_loopback_debugfs_open,
.read = pcie_loopback_debugfs_read,
};
//tianyan@2021.10.21 modify for pcie loopback test end
static int mtk_pcie_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct resource *reg_res;
struct mtk_pcie_phy *pcie_phy;
pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL);
if (!pcie_phy)
return -ENOMEM;
pcie_phy->dev = dev;
//tianyan@2021.7.27 modify for add wifi6 module start
/* pcie_phy->pipe_clk = devm_clk_get(pcie_phy->dev, "pipe_clk");
if (IS_ERR(pcie_phy->pipe_clk)) {
dev_warn(dev, "pcie pipe_clk not found\n");
pcie_phy->pipe_clk = NULL;
}*/
//tianyan@2021.7.27 modify for add wifi6 module end
pm_runtime_enable(pcie_phy->dev);
reg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy-sif");
pcie_phy->sif_base = devm_ioremap_resource(dev, reg_res);
if (IS_ERR(pcie_phy->sif_base)) {
dev_warn(dev, "failed to map pcie phy base\n");
return PTR_ERR(pcie_phy->sif_base);
}
//tianyan@2021.10.21 modify for pcie loopback test start
if(!pcie_loopback_test(pcie_phy->sif_base)){
pcie_loopback_test_res = true;
pr_info("pcie loopback test success! GPIO97 output high\n");
gpio_direction_output(268+97,1);
}
(void*)debugfs_create_file("pcie_loopback",
S_IFREG | 0444, NULL,
&pcie_loopback_test_res, &pcie_loopback_debugfs_ops);
//tianyan@2021.10.21 modify for pcie loopback test end
reg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy-ckm");
pcie_phy->ckm_base = devm_ioremap_resource(dev, reg_res);
if (IS_ERR(pcie_phy->ckm_base)) {
dev_warn(dev, "failed to map pcie phy base\n");
return PTR_ERR(pcie_phy->ckm_base);
}
platform_set_drvdata(pdev, pcie_phy);
pcie_phy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_phy_ops);
if (IS_ERR(pcie_phy->phy)) {
dev_err(dev, "failed to create pcie phy\n");
return PTR_ERR(pcie_phy->phy);
}
phy_set_drvdata(pcie_phy->phy, pcie_phy);
provider = devm_of_phy_provider_register(dev, mtk_pcie_phy_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static const struct of_device_id mtk_pcie_phy_id_table[] = {
{ .compatible = "mediatek,mt6880-pcie-phy" },
{ .compatible = "mediatek,mt2735-pcie-phy" },
{ },
};
static struct platform_driver mtk_pcie_phy_driver = {
.probe = mtk_pcie_phy_probe,
.driver = {
.name = "mtk-pcie-phy",
.of_match_table = mtk_pcie_phy_id_table,
},
};
module_platform_driver(mtk_pcie_phy_driver);
MODULE_LICENSE("GPL v2");