blob: 5c073fa000ffa82abd3511fa3611fdd3982ccd48 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 ASR Ltd
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/pci.h>
#include <linux/cputype.h>
#define PUPHY_CLK_CFG 0x408
#define PUPHY_MODE_CFG 0x40c
#define PUPHY_CAL_REG 0x420
#define PUPHY_PU_SEL 0x440
#define PUPHY_PLL_REG1 0x448
#define PUPHY_PLL_REG2 0x44c
#define PUPHY_RX_REG1 0x450
#define PUPHY_RX_REG2 0x454
#define PUPHY_TX_REG 0x464
#define PUPHY_READONLY_REG 0x484
#define FALCON_NR_PCIE_PHYS 1
struct falcon_pcie_phy {
struct device *dev;
void __iomem *base;
int nphys;
struct phy_pcie_instance {
struct phy *phy;
u32 index;
void __iomem *phy_base;
} phys[FALCON_NR_PCIE_PHYS];
};
static int falcon_pcie_phy_init(struct phy *phy)
{
struct phy_pcie_instance *inst = phy_get_drvdata(phy);
u32 val;
int err;
#ifdef CONFIG_CPU_ASR1903
int count = 1000;
#endif
#ifndef CONFIG_CPU_ASR1903
/* cfg_ss_mode_delay=1 */
val = readl(inst->phy_base + PUPHY_MODE_CFG);
writel((val&(~(0x3<<14))) | (0x1<<14), inst->phy_base + PUPHY_MODE_CFG);
/* disable puphy ssc */
val = readl(inst->phy_base + PUPHY_PLL_REG1);
writel((val& ~(0xf<<16)), inst->phy_base + PUPHY_PLL_REG1);
/* setting clock en and freq */
val = readl(inst->phy_base + PUPHY_CLK_CFG);
writel(val|(0xf<<3), inst->phy_base + PUPHY_CLK_CFG);
if (cpu_is_asr1828()) {
val = readl(inst->phy_base + PUPHY_PU_SEL);
val &= 0xfffffeff;
writel(val, inst->phy_base + PUPHY_PU_SEL);
val = readl(inst->phy_base + PUPHY_PLL_REG2);
val &= 0xfffff7ff;
writel(val, inst->phy_base + PUPHY_PLL_REG2);
}
#else
/* waiting PCIe readonly_reg2[2] r_tune_done==1 */
val = readl(inst->phy_base + PUPHY_READONLY_REG);
while (count--) {
val = readl(inst->phy_base + PUPHY_READONLY_REG);
mdelay(1);
if (val>>10 & 0x1)
break;
}
if (count < 0) {
pr_info("wait phy tune done timeout\n");
return -EINVAL;
}
/* copy readonly1[3:0] to rx reg1[3:0], and rx_reg4[5]=0 */
val = readl(inst->phy_base + PUPHY_READONLY_REG);
val &= ~(0xf<<8);
writel(val, inst->phy_base + PUPHY_RX_REG1);
val = readl(inst->phy_base + PUPHY_RX_REG1);
val = (val&0xf)<<8;
writel(val, inst->phy_base + PUPHY_RX_REG1);
val = readl(inst->phy_base + PUPHY_RX_REG2);
val &= ~(0x1<<5);
writel(val, inst->phy_base + PUPHY_RX_REG2);
/* copy readonly[7:4] to tx reg1[7:4], and tx_reg3[1]=1 */
val = readl(inst->phy_base + PUPHY_TX_REG);
val &= ~(0xf<<12);
writel(val, inst->phy_base + PUPHY_TX_REG);
val = readl(inst->phy_base + PUPHY_TX_REG);
val |= (((val>>4)&0xf)<<12);
writel(val, inst->phy_base + PUPHY_TX_REG);
val = readl(inst->phy_base + PUPHY_TX_REG);
val |= (0x1<<25);
writel(val, inst->phy_base + PUPHY_TX_REG);
/* rc_cal_reg2 clk sel, select input clock 38.4M */
val = readl(inst->phy_base + PUPHY_CAL_REG);
val &= ~(0x7<<29);
val |= (0x7<<29);
writel(val, inst->phy_base + PUPHY_CAL_REG);
val = readl(inst->phy_base + PUPHY_CAL_REG);
val &= ~(0x1<<22);
val |= (0x1<<22);
writel(val, inst->phy_base + PUPHY_CAL_REG);
/* disable puphy ssc */
val = readl(inst->phy_base + PUPHY_PLL_REG1);
writel((val& ~(0xf<<16)), inst->phy_base + PUPHY_PLL_REG1);
/* pll_reg2[7:5] freq_sel=38.4M, apply local 38.4M clock, that is the default value */
val = readl(inst->phy_base + PUPHY_PLL_REG1);
val &= ~(0x7<<13);
val |= (0x6<<13);
writel(val, inst->phy_base + PUPHY_PLL_REG1);
/* en_rterm refclk receiver r termination enable, only used in refclk reciever mode */
val = readl(inst->phy_base + PUPHY_RX_REG1);
val &= ~(0x1<<3);
writel(val, inst->phy_base + PUPHY_RX_REG1);
#endif
/* setting cfg_sw_phy_init done */
val = readl(inst->phy_base + PUPHY_CLK_CFG);
writel(val|(0x1<<11), inst->phy_base + PUPHY_CLK_CFG);
err = readl_poll_timeout_atomic(inst->phy_base + PUPHY_CLK_CFG, val,
(val & 0x1), 10, 10 * USEC_PER_MSEC);
if (err) {
pr_info("PCIe PLL isn't ready in time\n");
return PCIBIOS_SET_FAILED;
}
pr_debug("PCIE PLL is ready\n");
return PCIBIOS_SUCCESSFUL;
}
static const struct phy_ops falcon_pcie_phy_ops = {
.init = falcon_pcie_phy_init,
.owner = THIS_MODULE,
};
static struct phy *falcon_pcie_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct falcon_pcie_phy *falcon_phy = dev_get_drvdata(dev);
if (args->args_count == 0)
return falcon_phy->phys[0].phy;
return falcon_phy->phys[args->args[0]].phy;
}
static int falcon_pcie_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child_np;
struct phy_provider *provider;
struct falcon_pcie_phy *falcon_phy;
struct resource res;
unsigned int phy_idx = 0;
void __iomem *base_phy;
int retval;
falcon_phy = devm_kzalloc(dev, sizeof(*falcon_phy), GFP_KERNEL);
if (!falcon_phy)
return -ENOMEM;
falcon_phy->dev = dev;
falcon_phy->nphys = of_get_child_count(np);
for_each_child_of_node(np, child_np) {
falcon_phy->phys[phy_idx].phy = devm_phy_create(dev, dev->of_node, &falcon_pcie_phy_ops);
if (IS_ERR(falcon_phy->phys[phy_idx].phy)) {
dev_err(dev, "failed to create PCIe PHY\n");
return PTR_ERR(falcon_phy->phys[phy_idx].phy);
}
falcon_phy->phys[phy_idx].index = phy_idx;
retval = of_address_to_resource(child_np, 0, &res);
if (retval) {
dev_err(dev, "failed to get address resource\n");
goto put_child;
}
base_phy = devm_ioremap_resource(&(falcon_phy->phys[phy_idx].phy)->dev, &res);
if (IS_ERR(base_phy)) {
dev_err(dev, "failed to remap phy regs\n");
retval = PTR_ERR(base_phy);
goto put_child;
}
falcon_phy->phys[phy_idx].phy_base = base_phy;
phy_set_drvdata(falcon_phy->phys[phy_idx].phy, &falcon_phy->phys[phy_idx]);
phy_idx++;
}
platform_set_drvdata(pdev, falcon_phy);
provider = devm_of_phy_provider_register(dev, falcon_pcie_phy_xlate);
if (IS_ERR(provider)) {
dev_err(dev, "failed to register PHY provider\n");
return PTR_ERR(provider);
}
dev_info(dev, "Falcon PCIe PHY driver initialized\n");
return 0;
put_child:
of_node_put(child_np);
return retval;
}
static const struct of_device_id falcon_pcie_phy_match_table[] = {
{ .compatible = "asr,falcon-pcie-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, falcon_pcie_phy_match_table);
static struct platform_driver falcon_pcie_phy_driver = {
.driver = {
.name = "falcon-pcie-phy",
.of_match_table = falcon_pcie_phy_match_table,
},
.probe = falcon_pcie_phy_probe,
};
module_platform_driver(falcon_pcie_phy_driver);
MODULE_DESCRIPTION("ASR Falcon PCIe PHY driver");
MODULE_LICENSE("GPL v2");