drivers/phy/phy-google-usb.c

Source file repositories/reference/linux-study-clean/drivers/phy/phy-google-usb.c

File Facts

System
Linux kernel
Corpus path
drivers/phy/phy-google-usb.c
Extension
.c
Size
8293 bytes
Lines
297
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 google_usb_phy_instance {
	struct google_usb_phy *parent;
	unsigned int index;
	struct phy *phy;
	unsigned int num_clks;
	struct clk_bulk_data *clks;
	unsigned int num_rsts;
	struct reset_control_bulk_data *rsts;
};

struct google_usb_phy {
	struct device *dev;
	struct regmap *usb_cfg_regmap;
	unsigned int usb2_cfg_offset;
	void __iomem *usbdp_top_base;
	struct google_usb_phy_instance *insts;
	/*
	 * Protect phy registers from concurrent access, specifically via
	 * google_usb_set_orientation callback.
	 */
	struct mutex phy_mutex;
	struct typec_switch_dev *sw;
	enum typec_orientation orientation;
};

static void set_vbus_valid(struct google_usb_phy *gphy)
{
	u32 reg;

	if (gphy->orientation == TYPEC_ORIENTATION_NONE) {
		reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
		reg &= ~USBCS_PHY_CFG1_SYS_VBUSVALID;
		writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
	} else {
		reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
		reg |= USBCS_PHY_CFG1_SYS_VBUSVALID;
		writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
	}
}

static int google_usb_set_orientation(struct typec_switch_dev *sw,
				      enum typec_orientation orientation)
{
	struct google_usb_phy *gphy = typec_switch_get_drvdata(sw);

	dev_dbg(gphy->dev, "set orientation %d\n", orientation);

	gphy->orientation = orientation;

	if (pm_runtime_suspended(gphy->dev))
		return 0;

	guard(mutex)(&gphy->phy_mutex);

	set_vbus_valid(gphy);

	return 0;
}

static int google_usb2_phy_init(struct phy *_phy)
{
	struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
	struct google_usb_phy *gphy = inst->parent;
	u32 reg;
	int ret;

	dev_dbg(gphy->dev, "initializing usb2 phy\n");

	guard(mutex)(&gphy->phy_mutex);

	regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg);
	reg &= ~USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL;
	reg &= ~USBCS_USB2PHY_CFG21_REF_FREQ_SEL;
	reg |= FIELD_PREP(USBCS_USB2PHY_CFG21_REF_FREQ_SEL, 0);
	regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg);

	regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG19_OFFSET, &reg);
	reg &= ~USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV;
	reg |= FIELD_PREP(USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV, 368);
	regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG19_OFFSET, reg);

	set_vbus_valid(gphy);

	ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks);
	if (ret)
		return ret;

	ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts);
	if (ret) {
		clk_bulk_disable_unprepare(inst->num_clks, inst->clks);

Annotation

Implementation Notes