brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 734c8e0 Raw
205 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * StarFive JH7110 PCIe 2.0 PHY driver4 *5 * Copyright (C) 2023 StarFive Technology Co., Ltd.6 * Author: Minda Chen <minda.chen@starfivetech.com>7 */8 9#include <linux/bits.h>10#include <linux/clk.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/mfd/syscon.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18 19#define PCIE_KVCO_LEVEL_OFF		0x2820#define PCIE_USB3_PHY_PLL_CTL_OFF	0x7c21#define PCIE_KVCO_TUNE_SIGNAL_OFF	0x8022#define PCIE_USB3_PHY_ENABLE		BIT(4)23#define PHY_KVCO_FINE_TUNE_LEVEL	0x9124#define PHY_KVCO_FINE_TUNE_SIGNALS	0xc25 26#define USB_PDRSTN_SPLIT		BIT(17)27 28#define PCIE_PHY_MODE			BIT(20)29#define PCIE_PHY_MODE_MASK		GENMASK(21, 20)30#define PCIE_USB3_BUS_WIDTH_MASK	GENMASK(3, 2)31#define PCIE_USB3_BUS_WIDTH		BIT(3)32#define PCIE_USB3_RATE_MASK		GENMASK(6, 5)33#define PCIE_USB3_RX_STANDBY_MASK	BIT(7)34#define PCIE_USB3_PHY_ENABLE		BIT(4)35 36struct jh7110_pcie_phy {37	struct phy *phy;38	struct regmap *stg_syscon;39	struct regmap *sys_syscon;40	void __iomem *regs;41	u32 sys_phy_connect;42	u32 stg_pcie_mode;43	u32 stg_pcie_usb;44	enum phy_mode mode;45};46 47static int phy_usb3_mode_set(struct jh7110_pcie_phy *data)48{49	if (!data->stg_syscon || !data->sys_syscon) {50		dev_err(&data->phy->dev, "doesn't support usb3 mode\n");51		return -EINVAL;52	}53 54	regmap_update_bits(data->stg_syscon, data->stg_pcie_mode,55			   PCIE_PHY_MODE_MASK, PCIE_PHY_MODE);56	regmap_update_bits(data->stg_syscon, data->stg_pcie_usb,57			   PCIE_USB3_BUS_WIDTH_MASK, 0);58	regmap_update_bits(data->stg_syscon, data->stg_pcie_usb,59			   PCIE_USB3_PHY_ENABLE, PCIE_USB3_PHY_ENABLE);60 61	/* Connect usb 3.0 phy mode */62	regmap_update_bits(data->sys_syscon, data->sys_phy_connect,63			   USB_PDRSTN_SPLIT, 0);64 65	/* Configuare spread-spectrum mode: down-spread-spectrum */66	writel(PCIE_USB3_PHY_ENABLE, data->regs + PCIE_USB3_PHY_PLL_CTL_OFF);67 68	return 0;69}70 71static void phy_pcie_mode_set(struct jh7110_pcie_phy *data)72{73	u32 val;74 75	/* default is PCIe mode */76	if (!data->stg_syscon || !data->sys_syscon)77		return;78 79	regmap_update_bits(data->stg_syscon, data->stg_pcie_mode,80			   PCIE_PHY_MODE_MASK, 0);81	regmap_update_bits(data->stg_syscon, data->stg_pcie_usb,82			   PCIE_USB3_BUS_WIDTH_MASK,83			   PCIE_USB3_BUS_WIDTH);84	regmap_update_bits(data->stg_syscon, data->stg_pcie_usb,85			   PCIE_USB3_PHY_ENABLE, 0);86 87	regmap_update_bits(data->sys_syscon, data->sys_phy_connect,88			   USB_PDRSTN_SPLIT, 0);89 90	val = readl(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF);91	val &= ~PCIE_USB3_PHY_ENABLE;92	writel(val, data->regs + PCIE_USB3_PHY_PLL_CTL_OFF);93}94 95static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy)96{97	/* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */98	writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF);99	writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF);100}101 102static int jh7110_pcie_phy_set_mode(struct phy *_phy,103				    enum phy_mode mode, int submode)104{105	struct jh7110_pcie_phy *phy = phy_get_drvdata(_phy);106	int ret;107 108	if (mode == phy->mode)109		return 0;110 111	switch (mode) {112	case PHY_MODE_USB_HOST:113	case PHY_MODE_USB_DEVICE:114	case PHY_MODE_USB_OTG:115		ret = phy_usb3_mode_set(phy);116		if (ret)117			return ret;118		break;119	case PHY_MODE_PCIE:120		phy_pcie_mode_set(phy);121		break;122	default:123		return -EINVAL;124	}125 126	dev_dbg(&_phy->dev, "Changing phy mode to %d\n", mode);127	phy->mode = mode;128 129	return 0;130}131 132static const struct phy_ops jh7110_pcie_phy_ops = {133	.set_mode	= jh7110_pcie_phy_set_mode,134	.owner		= THIS_MODULE,135};136 137static int jh7110_pcie_phy_probe(struct platform_device *pdev)138{139	struct jh7110_pcie_phy *phy;140	struct device *dev = &pdev->dev;141	struct phy_provider *phy_provider;142	u32 args[2];143 144	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);145	if (!phy)146		return -ENOMEM;147 148	phy->regs = devm_platform_ioremap_resource(pdev, 0);149	if (IS_ERR(phy->regs))150		return PTR_ERR(phy->regs);151 152	phy->phy = devm_phy_create(dev, NULL, &jh7110_pcie_phy_ops);153	if (IS_ERR(phy->phy))154		return dev_err_probe(dev, PTR_ERR(phy->phy),155				     "Failed to map phy base\n");156 157	phy->sys_syscon =158		syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node,159						     "starfive,sys-syscon",160						     1, args);161 162	if (!IS_ERR_OR_NULL(phy->sys_syscon))163		phy->sys_phy_connect = args[0];164	else165		phy->sys_syscon = NULL;166 167	phy->stg_syscon =168		syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node,169						     "starfive,stg-syscon",170						     2, args);171 172	if (!IS_ERR_OR_NULL(phy->stg_syscon)) {173		phy->stg_pcie_mode = args[0];174		phy->stg_pcie_usb = args[1];175	} else {176		phy->stg_syscon = NULL;177	}178 179	phy_kvco_gain_set(phy);180 181	phy_set_drvdata(phy->phy, phy);182	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);183 184	return PTR_ERR_OR_ZERO(phy_provider);185}186 187static const struct of_device_id jh7110_pcie_phy_of_match[] = {188	{ .compatible = "starfive,jh7110-pcie-phy" },189	{ /* sentinel */ },190};191MODULE_DEVICE_TABLE(of, jh7110_pcie_phy_of_match);192 193static struct platform_driver jh7110_pcie_phy_driver = {194	.probe	= jh7110_pcie_phy_probe,195	.driver = {196		.of_match_table	= jh7110_pcie_phy_of_match,197		.name  = "jh7110-pcie-phy",198	}199};200module_platform_driver(jh7110_pcie_phy_driver);201 202MODULE_DESCRIPTION("StarFive JH7110 PCIe 2.0 PHY driver");203MODULE_AUTHOR("Minda Chen <minda.chen@starfivetech.com>");204MODULE_LICENSE("GPL");205