/* set of registers with offsets different per-PHY */ enum qphy_reg_layout { /* Common block control registers */
QPHY_COM_SW_RESET,
QPHY_COM_POWER_DOWN_CONTROL,
QPHY_COM_START_CONTROL,
QPHY_COM_PCS_READY_STATUS, /* PCS registers */
QPHY_SW_RESET,
QPHY_START_CTRL,
QPHY_PCS_STATUS, /* Keep last to ensure regs_layout arrays are properly initialized */
QPHY_LAYOUT_SIZE
};
/* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* number of PHYs provided by this block */ int num_phys;
/* Init sequence for PHY blocks - serdes, tx, rx, pcs */ conststruct qmp_phy_init_tbl *serdes_tbl; int serdes_tbl_num; conststruct qmp_phy_init_tbl *tx_tbl; int tx_tbl_num; conststruct qmp_phy_init_tbl *rx_tbl; int rx_tbl_num; conststruct qmp_phy_init_tbl *pcs_tbl; int pcs_tbl_num;
/* clock ids to be requested */ constchar * const *clk_list; int num_clks; /* resets to be requested */ constchar * const *reset_list; int num_resets; /* regulators to be requested */ constchar * const *vreg_list; int num_vregs;
/* array of registers with different offsets */ constunsignedint *regs;
};
/** * struct qmp_phy - per-lane phy descriptor * * @phy: generic phy * @cfg: phy specific configuration * @serdes: iomapped memory space for phy's serdes (i.e. PLL) * @tx: iomapped memory space for lane's tx * @rx: iomapped memory space for lane's rx * @pcs: iomapped memory space for lane's pcs * @pipe_clk: pipe clock * @index: lane index * @qmp: QMP phy to which this lane belongs * @lane_rst: lane's reset controller
*/ struct qmp_phy { struct phy *phy; conststruct qmp_phy_cfg *cfg; void __iomem *serdes; void __iomem *tx; void __iomem *rx; void __iomem *pcs; struct clk *pipe_clk; unsignedint index; struct qcom_qmp *qmp; struct reset_control *lane_rst;
};
/** * struct qcom_qmp - structure holding QMP phy block attributes * * @dev: device * * @clks: array of clocks required by phy * @resets: array of resets required by phy * @vregs: regulator supplies bulk data * * @phys: array of per-lane phy descriptors * @phy_mutex: mutex lock for PHY common block initialization * @init_count: phy common block initialization count
*/ struct qcom_qmp { struct device *dev;
/* * Pull out PHY from POWER DOWN state. * This is active low enable signal to power-down PHY.
*/
qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
SW_PWRDN | REFCLK_DRV_DSBL);
/* * Register a fixed rate pipe clock. * * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate * controls it. The <s>_pipe_clk coming out of the GCC is requested * by the PHY driver for its operations. * We register the <s>_pipe_clksrc here. The gcc driver takes care * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. * Below picture shows this relationship. * * +---------------+ * | PHY block |<<---------------------------------------+ * | | | * | +-------+ | +-----+ | * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ * clk | +-------+ | +-----+ * +---------------+
*/ staticint phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
{ struct clk_fixed_rate *fixed; struct clk_init_data init = { }; int ret;
ret = of_property_read_string(np, "clock-output-names", &init.name); if (ret) {
dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); return ret;
}
fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); if (!fixed) return -ENOMEM;
init.ops = &clk_fixed_rate_ops;
/* controllers using QMP phys use 125MHz pipe clock interface */
fixed->fixed_rate = 125000000;
fixed->hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &fixed->hw); if (ret) return ret;
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); if (ret) return ret;
/* * Roll a devm action because the clock provider is the child node, but * the child node is not actually a device.
*/ return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
}
qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); if (!qmp) return -ENOMEM;
qmp->dev = dev;
dev_set_drvdata(dev, qmp);
cfg = of_device_get_match_data(dev); if (!cfg) return -EINVAL;
serdes = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(serdes)) return PTR_ERR(serdes);
expected_phys = cfg->num_phys;
mutex_init(&qmp->phy_mutex);
ret = qmp_pcie_msm8996_clk_init(dev, cfg); if (ret) return ret;
ret = qmp_pcie_msm8996_reset_init(dev, cfg); if (ret) return ret;
ret = qmp_pcie_msm8996_vreg_init(dev, cfg); if (ret) return ret;
num = of_get_available_child_count(dev->of_node); /* do we have a rogue child node ? */ if (num > expected_phys) return -EINVAL;
qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); if (!qmp->phys) return -ENOMEM;
id = 0;
for_each_available_child_of_node_scoped(dev->of_node, child) { /* Create per-lane phy */
ret = qmp_pcie_msm8996_create(dev, child, id, serdes, cfg); if (ret) {
dev_err(dev, "failed to create lane%d phy, %d\n",
id, ret); return ret;
}
/* * Register the pipe clock provided by phy. * See function description to see details of this pipe clock.
*/
ret = phy_pipe_clk_register(qmp, child); if (ret) {
dev_err(qmp->dev, "failed to register pipe clock source\n"); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.