// SPDX-License-Identifier: GPL-2.0-or-later /* * Rockchip USB2.0 PHY with Innosilicon IP block driver * * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd */ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/extcon-provider.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/gpio/consumer.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/mfd/syscon.h> #include <linux/usb/of.h> #include <linux/usb/otg.h> #define BIT_WRITEABLE_SHIFT … #define SCHEDULE_DELAY … #define OTG_SCHEDULE_DELAY … struct rockchip_usb2phy; enum rockchip_usb2phy_port_id { … }; enum rockchip_usb2phy_host_state { … }; /** * enum usb_chg_state - Different states involved in USB charger detection. * @USB_CHG_STATE_UNDEFINED: USB charger is not connected or detection * process is not yet started. * @USB_CHG_STATE_WAIT_FOR_DCD: Waiting for Data pins contact. * @USB_CHG_STATE_DCD_DONE: Data pin contact is detected. * @USB_CHG_STATE_PRIMARY_DONE: Primary detection is completed (Detects * between SDP and DCP/CDP). * @USB_CHG_STATE_SECONDARY_DONE: Secondary detection is completed (Detects * between DCP and CDP). * @USB_CHG_STATE_DETECTED: USB charger type is determined. */ enum usb_chg_state { … }; static const unsigned int rockchip_usb2phy_extcon_cable[] = …; struct usb2phy_reg { … }; /** * struct rockchip_chg_det_reg - usb charger detect registers * @cp_det: charging port detected successfully. * @dcp_det: dedicated charging port detected successfully. * @dp_det: assert data pin connect successfully. * @idm_sink_en: open dm sink curren. * @idp_sink_en: open dp sink current. * @idp_src_en: open dm source current. * @rdm_pdwn_en: open dm pull down resistor. * @vdm_src_en: open dm voltage source. * @vdp_src_en: open dp voltage source. * @opmode: utmi operational mode. */ struct rockchip_chg_det_reg { … }; /** * struct rockchip_usb2phy_port_cfg - usb-phy port configuration. * @phy_sus: phy suspend register. * @bvalid_det_en: vbus valid rise detection enable register. * @bvalid_det_st: vbus valid rise detection status register. * @bvalid_det_clr: vbus valid rise detection clear register. * @disfall_en: host disconnect fall edge detection enable. * @disfall_st: host disconnect fall edge detection state. * @disfall_clr: host disconnect fall edge detection clear. * @disrise_en: host disconnect rise edge detection enable. * @disrise_st: host disconnect rise edge detection state. * @disrise_clr: host disconnect rise edge detection clear. * @idfall_det_en: id detection enable register, falling edge * @idfall_det_st: id detection state register, falling edge * @idfall_det_clr: id detection clear register, falling edge * @idrise_det_en: id detection enable register, rising edge * @idrise_det_st: id detection state register, rising edge * @idrise_det_clr: id detection clear register, rising edge * @ls_det_en: linestate detection enable register. * @ls_det_st: linestate detection state register. * @ls_det_clr: linestate detection clear register. * @utmi_avalid: utmi vbus avalid status register. * @utmi_bvalid: utmi vbus bvalid status register. * @utmi_id: utmi id state register. * @utmi_ls: utmi linestate state register. * @utmi_hstdet: utmi host disconnect register. */ struct rockchip_usb2phy_port_cfg { … }; /** * struct rockchip_usb2phy_cfg - usb-phy configuration. * @reg: the address offset of grf for usb-phy config. * @num_ports: specify how many ports that the phy has. * @phy_tuning: phy default parameters tuning. * @clkout_ctl: keep on/turn off output clk of phy. * @port_cfgs: usb-phy port configurations. * @chg_det: charger detection registers. */ struct rockchip_usb2phy_cfg { … }; /** * struct rockchip_usb2phy_port - usb-phy port data. * @phy: generic phy. * @port_id: flag for otg port or host port. * @suspended: phy suspended flag. * @vbus_attached: otg device vbus status. * @host_disconnect: usb host disconnect status. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @id_irq: IRQ number assigned for ID pin detection. * @ls_irq: IRQ number assigned for linestate detection. * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate * irqs to one irq in otg-port. * @mutex: for register updating in sm_work. * @chg_work: charge detect work. * @otg_sm_work: OTG state machine work. * @sm_work: HOST state machine work. * @port_cfg: port register configuration, assigned by driver data. * @event_nb: hold event notification callback. * @state: define OTG enumeration states before device reset. * @mode: the dr_mode of the controller. */ struct rockchip_usb2phy_port { … }; /** * struct rockchip_usb2phy - usb2.0 phy driver data. * @dev: pointer to device. * @grf: General Register Files regmap. * @usbgrf: USB General Register Files regmap. * @clk: clock struct of phy input clk. * @clk480m: clock struct of phy output clk. * @clk480m_hw: clock struct of phy output clk management. * @phy_reset: phy reset control. * @chg_state: states involved in USB charger detection. * @chg_type: USB charger types. * @dcd_retries: The retry count used to track Data contact * detection process. * @edev: extcon device for notification registration * @irq: muxed interrupt for single irq configuration * @phy_cfg: phy register configuration, assigned by driver data. * @ports: phy port instance. */ struct rockchip_usb2phy { … }; static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy) { … } static inline int property_enable(struct regmap *base, const struct usb2phy_reg *reg, bool en) { … } static inline bool property_enabled(struct regmap *base, const struct usb2phy_reg *reg) { … } static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy) { … } static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) { … } static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) { … } static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) { … } static unsigned long rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { … } static const struct clk_ops rockchip_usb2phy_clkout_ops = …; static void rockchip_usb2phy_clk480m_unregister(void *data) { … } static int rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) { … } static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) { … } static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, bool en) { … } static int rockchip_usb2phy_init(struct phy *phy) { … } static int rockchip_usb2phy_power_on(struct phy *phy) { … } static int rockchip_usb2phy_power_off(struct phy *phy) { … } static int rockchip_usb2phy_exit(struct phy *phy) { … } static const struct phy_ops rockchip_usb2phy_ops = …; static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) { … } static const char *chg_to_string(enum power_supply_type chg_type) { … } static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, bool en) { … } static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, bool en) { … } static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, bool en) { … } #define CHG_DCD_POLL_TIME … #define CHG_DCD_MAX_RETRIES … #define CHG_PRIMARY_DET_TIME … #define CHG_SECONDARY_DET_TIME … static void rockchip_chg_detect_work(struct work_struct *work) { … } /* * The function manage host-phy port state and suspend/resume phy port * to save power. * * we rely on utmi_linestate and utmi_hostdisconnect to identify whether * devices is disconnect or not. Besides, we do not need care it is FS/LS * disconnected or HS disconnected, actually, we just only need get the * device is disconnected at last through rearm the delayed work, * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. * * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke * some clk related APIs, so do not invoke it from interrupt context directly. */ static void rockchip_usb2phy_sm_work(struct work_struct *work) { … } static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) { … } static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) { … } static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) { … } static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) { … } static irqreturn_t rockchip_usb2phy_host_disc_irq(int irq, void *data) { … } static irqreturn_t rockchip_usb2phy_irq(int irq, void *data) { … } static int rockchip_usb2phy_port_irq_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) { … } static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) { … } static int rockchip_otg_event(struct notifier_block *nb, unsigned long event, void *ptr) { … } static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) { … } static int rockchip_usb2phy_probe(struct platform_device *pdev) { … } static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) { … } static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) { … } static const struct rockchip_usb2phy_cfg rk3128_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = …; static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = …; static const struct of_device_id rockchip_usb2phy_dt_match[] = …; MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); static struct platform_driver rockchip_usb2phy_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;