drivers/phy/ralink/phy-mt7621-pci.c

Source file repositories/reference/linux-study-clean/drivers/phy/ralink/phy-mt7621-pci.c

File Facts

System
Linux kernel
Corpus path
drivers/phy/ralink/phy-mt7621-pci.c
Extension
.c
Size
9972 bytes
Lines
360
Domain
Driver Families
Bucket
drivers/phy
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct mt7621_pci_phy {
	struct device *dev;
	struct regmap *regmap;
	struct phy *phy;
	struct clk *sys_clk;
	void __iomem *port_base;
	bool has_dual_port;
	bool bypass_pipe_rst;
};

static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy,
				  u32 reg, u32 clr, u32 set)
{
	u32 val;

	/*
	 * We cannot use 'regmap_write_bits' here because internally
	 * 'set' is masked before is set to the value that will be
	 * written to the register. That way results in no reliable
	 * pci setup. Avoid to mask 'set' before set value to 'val'
	 * completely avoid the problem.
	 */
	regmap_read(phy->regmap, reg, &val);
	val &= ~clr;
	val |= set;
	regmap_write(phy->regmap, reg, val);
}

static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy)
{
	mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST);
	mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC);

	if (phy->has_dual_port) {
		mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,
			       0, RG_PE1_PIPE_RST);
		mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,
			       0, RG_PE1_PIPE_CMD_FRC);
	}
}

static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy)
{
	struct device *dev = phy->dev;
	unsigned long clk_rate;

	clk_rate = clk_get_rate(phy->sys_clk);
	if (!clk_rate)
		return -EINVAL;

	/* Set PCIe Port PHY to disable SSC */
	/* Debug Xtal Type */
	mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG,
		       RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE,
		       RG_PE1_FRC_H_XTAL_TYPE |
		       FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00));

	/* disable port */
	mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN,
		       RG_PE1_FRC_PHY_EN);

	if (phy->has_dual_port) {
		mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
			       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
	}

	if (clk_rate == 40000000) { /* 40MHz Xtal */
		/* Set Pre-divider ratio (for host mode) */
		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,
			       FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01));

		dev_dbg(dev, "Xtal is 40MHz\n");
	} else if (clk_rate == 25000000) { /* 25MHz Xal */
		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,
			       FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00));

		/* Select feedback clock */
		mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG,
			       RG_PE1_H_PLL_FBKSEL,
			       FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01));

		/* DDS NCPO PCW (for host mode) */
		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,
			       RG_PE1_H_LCDDS_SSC_PRD,
			       FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00));

		/* DDS SSC dither period control */
		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,
			       RG_PE1_H_LCDDS_SSC_PRD,
			       FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d));

Annotation

Implementation Notes