brintos

brintos / linux-shallow public Read only

0
0
Text · 9.7 KiB · a591ad9 Raw
360 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Mediatek MT7621 PCI PHY Driver4 * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com>5 */6 7#include <dt-bindings/phy/phy.h>8#include <linux/clk.h>9#include <linux/bitfield.h>10#include <linux/bitops.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <linux/sys_soc.h>17 18#define RG_PE1_PIPE_REG				0x02c19#define RG_PE1_PIPE_RST				BIT(12)20#define RG_PE1_PIPE_CMD_FRC			BIT(4)21 22#define RG_P0_TO_P1_WIDTH			0x10023#define RG_PE1_H_LCDDS_REG			0x49c24#define RG_PE1_H_LCDDS_PCW			GENMASK(30, 0)25 26#define RG_PE1_FRC_H_XTAL_REG			0x40027#define RG_PE1_FRC_H_XTAL_TYPE			BIT(8)28#define RG_PE1_H_XTAL_TYPE			GENMASK(10, 9)29 30#define RG_PE1_FRC_PHY_REG			0x00031#define RG_PE1_FRC_PHY_EN			BIT(4)32#define RG_PE1_PHY_EN				BIT(5)33 34#define RG_PE1_H_PLL_REG			0x49035#define RG_PE1_H_PLL_BC				GENMASK(23, 22)36#define RG_PE1_H_PLL_BP				GENMASK(21, 18)37#define RG_PE1_H_PLL_IR				GENMASK(15, 12)38#define RG_PE1_H_PLL_IC				GENMASK(11, 8)39#define RG_PE1_H_PLL_PREDIV			GENMASK(7, 6)40#define RG_PE1_PLL_DIVEN			GENMASK(3, 1)41 42#define RG_PE1_H_PLL_FBKSEL_REG			0x4bc43#define RG_PE1_H_PLL_FBKSEL			GENMASK(5, 4)44 45#define	RG_PE1_H_LCDDS_SSC_PRD_REG		0x4a446#define RG_PE1_H_LCDDS_SSC_PRD			GENMASK(15, 0)47 48#define RG_PE1_H_LCDDS_SSC_DELTA_REG		0x4a849#define RG_PE1_H_LCDDS_SSC_DELTA		GENMASK(11, 0)50#define RG_PE1_H_LCDDS_SSC_DELTA1		GENMASK(27, 16)51 52#define RG_PE1_LCDDS_CLK_PH_INV_REG		0x4a053#define RG_PE1_LCDDS_CLK_PH_INV			BIT(5)54 55#define RG_PE1_H_PLL_BR_REG			0x4ac56#define RG_PE1_H_PLL_BR				GENMASK(18, 16)57 58#define	RG_PE1_MSTCKDIV_REG			0x41459#define RG_PE1_MSTCKDIV				GENMASK(7, 6)60 61#define RG_PE1_FRC_MSTCKDIV			BIT(5)62 63#define MAX_PHYS	264 65/**66 * struct mt7621_pci_phy - Mt7621 Pcie PHY core67 * @dev: pointer to device68 * @regmap: kernel regmap pointer69 * @phy: pointer to the kernel PHY device70 * @sys_clk: pointer to the system XTAL clock71 * @port_base: base register72 * @has_dual_port: if the phy has dual ports.73 * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst'74 * needs to be executed. Depends on chip revision.75 */76struct mt7621_pci_phy {77	struct device *dev;78	struct regmap *regmap;79	struct phy *phy;80	struct clk *sys_clk;81	void __iomem *port_base;82	bool has_dual_port;83	bool bypass_pipe_rst;84};85 86static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy,87				  u32 reg, u32 clr, u32 set)88{89	u32 val;90 91	/*92	 * We cannot use 'regmap_write_bits' here because internally93	 * 'set' is masked before is set to the value that will be94	 * written to the register. That way results in no reliable95	 * pci setup. Avoid to mask 'set' before set value to 'val'96	 * completely avoid the problem.97	 */98	regmap_read(phy->regmap, reg, &val);99	val &= ~clr;100	val |= set;101	regmap_write(phy->regmap, reg, val);102}103 104static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy)105{106	mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST);107	mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC);108 109	if (phy->has_dual_port) {110		mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,111			       0, RG_PE1_PIPE_RST);112		mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,113			       0, RG_PE1_PIPE_CMD_FRC);114	}115}116 117static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy)118{119	struct device *dev = phy->dev;120	unsigned long clk_rate;121 122	clk_rate = clk_get_rate(phy->sys_clk);123	if (!clk_rate)124		return -EINVAL;125 126	/* Set PCIe Port PHY to disable SSC */127	/* Debug Xtal Type */128	mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG,129		       RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE,130		       RG_PE1_FRC_H_XTAL_TYPE |131		       FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00));132 133	/* disable port */134	mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN,135		       RG_PE1_FRC_PHY_EN);136 137	if (phy->has_dual_port) {138		mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,139			       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);140	}141 142	if (clk_rate == 40000000) { /* 40MHz Xtal */143		/* Set Pre-divider ratio (for host mode) */144		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,145			       FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01));146 147		dev_dbg(dev, "Xtal is 40MHz\n");148	} else if (clk_rate == 25000000) { /* 25MHz Xal */149		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,150			       FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00));151 152		/* Select feedback clock */153		mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG,154			       RG_PE1_H_PLL_FBKSEL,155			       FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01));156 157		/* DDS NCPO PCW (for host mode) */158		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,159			       RG_PE1_H_LCDDS_SSC_PRD,160			       FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00));161 162		/* DDS SSC dither period control */163		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,164			       RG_PE1_H_LCDDS_SSC_PRD,165			       FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d));166 167		/* DDS SSC dither amplitude control */168		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG,169			       RG_PE1_H_LCDDS_SSC_DELTA |170			       RG_PE1_H_LCDDS_SSC_DELTA1,171			       FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA, 0x4a) |172			       FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA1, 0x4a));173 174		dev_dbg(dev, "Xtal is 25MHz\n");175	} else { /* 20MHz Xtal */176		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,177			       FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00));178 179		dev_dbg(dev, "Xtal is 20MHz\n");180	}181 182	/* DDS clock inversion */183	mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG,184		       RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV);185 186	/* Set PLL bits */187	mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,188		       RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR |189		       RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN,190		       FIELD_PREP(RG_PE1_H_PLL_BC, 0x02) |191		       FIELD_PREP(RG_PE1_H_PLL_BP, 0x06) |192		       FIELD_PREP(RG_PE1_H_PLL_IR, 0x02) |193		       FIELD_PREP(RG_PE1_H_PLL_IC, 0x01) |194		       FIELD_PREP(RG_PE1_PLL_DIVEN, 0x02));195 196	mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR,197		       FIELD_PREP(RG_PE1_H_PLL_BR, 0x00));198 199	if (clk_rate == 40000000) { /* 40MHz Xtal */200		/* set force mode enable of da_pe1_mstckdiv */201		mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG,202			       RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV,203			       FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) |204			       RG_PE1_FRC_MSTCKDIV);205	}206 207	return 0;208}209 210static int mt7621_pci_phy_init(struct phy *phy)211{212	struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);213 214	if (mphy->bypass_pipe_rst)215		mt7621_bypass_pipe_rst(mphy);216 217	return mt7621_set_phy_for_ssc(mphy);218}219 220static int mt7621_pci_phy_power_on(struct phy *phy)221{222	struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);223 224	/* Enable PHY and disable force mode */225	mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG,226		       RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN);227 228	if (mphy->has_dual_port) {229		mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,230			       RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN);231	}232 233	return 0;234}235 236static int mt7621_pci_phy_power_off(struct phy *phy)237{238	struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);239 240	/* Disable PHY */241	mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG,242		       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);243 244	if (mphy->has_dual_port) {245		mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,246			       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);247	}248 249	return 0;250}251 252static int mt7621_pci_phy_exit(struct phy *phy)253{254	return 0;255}256 257static const struct phy_ops mt7621_pci_phy_ops = {258	.init		= mt7621_pci_phy_init,259	.exit		= mt7621_pci_phy_exit,260	.power_on	= mt7621_pci_phy_power_on,261	.power_off	= mt7621_pci_phy_power_off,262	.owner		= THIS_MODULE,263};264 265static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev,266					    const struct of_phandle_args *args)267{268	struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev);269 270	if (WARN_ON(args->args[0] >= MAX_PHYS))271		return ERR_PTR(-ENODEV);272 273	mt7621_phy->has_dual_port = args->args[0];274 275	dev_dbg(dev, "PHY for 0x%px (dual port = %d)\n",276		mt7621_phy->port_base, mt7621_phy->has_dual_port);277 278	return mt7621_phy->phy;279}280 281static const struct soc_device_attribute mt7621_pci_quirks_match[] = {282	{ .soc_id = "mt7621", .revision = "E2" },283	{ /* sentinel */ }284};285 286static const struct regmap_config mt7621_pci_phy_regmap_config = {287	.reg_bits = 32,288	.val_bits = 32,289	.reg_stride = 4,290	.max_register = 0x700,291};292 293static int mt7621_pci_phy_probe(struct platform_device *pdev)294{295	struct device *dev = &pdev->dev;296	const struct soc_device_attribute *attr;297	struct phy_provider *provider;298	struct mt7621_pci_phy *phy;299 300	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);301	if (!phy)302		return -ENOMEM;303 304	attr = soc_device_match(mt7621_pci_quirks_match);305	if (attr)306		phy->bypass_pipe_rst = true;307 308	phy->dev = dev;309	platform_set_drvdata(pdev, phy);310 311	phy->port_base = devm_platform_ioremap_resource(pdev, 0);312	if (IS_ERR(phy->port_base)) {313		dev_err(dev, "failed to remap phy regs\n");314		return PTR_ERR(phy->port_base);315	}316 317	phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base,318					    &mt7621_pci_phy_regmap_config);319	if (IS_ERR(phy->regmap))320		return PTR_ERR(phy->regmap);321 322	phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops);323	if (IS_ERR(phy->phy)) {324		dev_err(dev, "failed to create phy\n");325		return PTR_ERR(phy->phy);326	}327 328	phy->sys_clk = devm_clk_get(dev, NULL);329	if (IS_ERR(phy->sys_clk)) {330		dev_err(dev, "failed to get phy clock\n");331		return PTR_ERR(phy->sys_clk);332	}333 334	phy_set_drvdata(phy->phy, phy);335 336	provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate);337 338	return PTR_ERR_OR_ZERO(provider);339}340 341static const struct of_device_id mt7621_pci_phy_ids[] = {342	{ .compatible = "mediatek,mt7621-pci-phy" },343	{},344};345MODULE_DEVICE_TABLE(of, mt7621_pci_phy_ids);346 347static struct platform_driver mt7621_pci_phy_driver = {348	.probe = mt7621_pci_phy_probe,349	.driver = {350		.name = "mt7621-pci-phy",351		.of_match_table = mt7621_pci_phy_ids,352	},353};354 355builtin_platform_driver(mt7621_pci_phy_driver);356 357MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>");358MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver");359MODULE_LICENSE("GPL v2");360