From d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:45:28 +0000 Subject: [PATCH] add boot partition size --- kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 1071 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 932 insertions(+), 139 deletions(-) diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 9d6dbbf..fe928d9 100644 --- a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1,17 +1,8 @@ +// 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 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/clk.h> @@ -34,10 +25,13 @@ #include <linux/pm_runtime.h> #include <linux/power_supply.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/rockchip/cpu.h> #include <linux/mfd/syscon.h> #include <linux/usb/of.h> #include <linux/usb/otg.h> +#include <linux/usb/role.h> +#include <linux/usb/typec_mux.h> #include <linux/wakelock.h> #define BIT_WRITEABLE_SHIFT 16 @@ -63,16 +57,16 @@ }; /** - * Different states involved in USB charger detection. - * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * 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 + * @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. + * @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 { USB_CHG_STATE_UNDEFINED = 0, @@ -103,7 +97,7 @@ }; /** - * struct rockchip_chg_det_reg: usb charger detect registers + * 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. @@ -129,18 +123,27 @@ }; /** - * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. + * struct rockchip_usb2phy_port_cfg - usb-phy port configuration. * @phy_sus: phy suspend register. + * @pipe_phystatus: select pipe phystatus from grf or phy. * @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. - * @bvalid_set: bvalid select and set to usb controller. + * @bvalid_grf_con: vbus valid software control. + * @bvalid_grf_sel: vbus valid software control select. + * @bvalid_phy_con: vbus valid external select and enable. * @bypass_dm_en: usb bypass uart DM enable register. * @bypass_sel: usb bypass uart select register. * @bypass_iomux: usb bypass uart GRF iomux register. * @bypass_bc: bypass battery charging module. * @bypass_otg: bypass otg module. * @bypass_host: bypass host module. + * @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. * @ls_det_en: linestate detection enable register. * @ls_det_st: linestate detection state register. * @ls_det_clr: linestate detection clear register. @@ -163,16 +166,25 @@ */ struct rockchip_usb2phy_port_cfg { struct usb2phy_reg phy_sus; + struct usb2phy_reg pipe_phystatus; struct usb2phy_reg bvalid_det_en; struct usb2phy_reg bvalid_det_st; struct usb2phy_reg bvalid_det_clr; - struct usb2phy_reg bvalid_set; + struct usb2phy_reg bvalid_grf_con; + struct usb2phy_reg bvalid_grf_sel; + struct usb2phy_reg bvalid_phy_con; struct usb2phy_reg bypass_dm_en; struct usb2phy_reg bypass_sel; struct usb2phy_reg bypass_iomux; struct usb2phy_reg bypass_bc; struct usb2phy_reg bypass_otg; struct usb2phy_reg bypass_host; + struct usb2phy_reg disfall_en; + struct usb2phy_reg disfall_st; + struct usb2phy_reg disfall_clr; + struct usb2phy_reg disrise_en; + struct usb2phy_reg disrise_st; + struct usb2phy_reg disrise_clr; struct usb2phy_reg ls_det_en; struct usb2phy_reg ls_det_st; struct usb2phy_reg ls_det_clr; @@ -194,21 +206,23 @@ }; /** - * struct rockchip_usb2phy_cfg: usb-phy configuration. + * 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 tunning. + * @phy_tuning: phy default parameters tuning. * @vbus_detect: vbus voltage level detection function. * @clkout_ctl: keep on/turn off output clk of phy via commonon bit. * @clkout_ctl_phy: keep on/turn off output clk of phy via phy inner * debug register. + * @ls_filter_con: set linestate filter time. + * @port_cfgs: usb-phy port configurations. * @ls_filter_con: set linestate filter time. * @chg_det: charger detection registers. */ struct rockchip_usb2phy_cfg { unsigned int reg; unsigned int num_ports; - int (*phy_tuning)(struct rockchip_usb2phy *); + int (*phy_tuning)(struct rockchip_usb2phy *rphy); int (*vbus_detect)(struct rockchip_usb2phy *rphy, const struct usb2phy_reg *vbus_det_en, bool en); @@ -220,19 +234,24 @@ }; /** - * struct rockchip_usb2phy_port: usb-phy port data. + * struct rockchip_usb2phy_port - usb-phy port data. + * @phy: generic phy. * @port_id: flag for otg port or host port. * @low_power_en: enable enter low power when suspend. * @perip_connected: flag for periphyeral connect status. * @prev_iddig: previous otg port id pin status. + * @sel_pipe_phystatus: select pipe phystatus from grf. * @suspended: phy suspended flag. + * @typec_vbus_det: Type-C otg vbus detect. * @utmi_avalid: utmi avalid status usage flag. * true - use avalid to get vbus status - * flase - use bvalid to get vbus status + * false - use bvalid to get vbus status * @vbus_attached: otg device vbus status. * @vbus_always_on: otg vbus is always powered on. * @vbus_enabled: vbus regulator status. * @bypass_uart_en: usb bypass uart enable, passed from DT. + * @host_disconnect: usb host disconnect status. + * @dis_u2_susphy: disable usb2 phy suspend. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @ls_irq: IRQ number assigned for linestate detection. * @id_irq: IRQ number assigned for id fall or rise detection. @@ -244,7 +263,8 @@ * @otg_sm_work: OTG state machine work. * @sm_work: HOST state machine work. * @vbus: vbus regulator supply on few rockchip boards. - * @phy_cfg: port register configuration, assigned by driver data. + * @sw: orientation switch, communicate with TCPM (Type-C Port Manager). + * @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. @@ -255,12 +275,15 @@ bool low_power_en; bool perip_connected; bool prev_iddig; + bool sel_pipe_phystatus; bool suspended; + bool typec_vbus_det; bool utmi_avalid; bool vbus_attached; bool vbus_always_on; bool vbus_enabled; bool bypass_uart_en; + bool host_disconnect; bool dis_u2_susphy; int bvalid_irq; int ls_irq; @@ -272,6 +295,7 @@ struct delayed_work otg_sm_work; struct delayed_work sm_work; struct regulator *vbus; + struct typec_switch *sw; const struct rockchip_usb2phy_port_cfg *port_cfg; struct notifier_block event_nb; struct wake_lock wakelock; @@ -280,13 +304,16 @@ }; /** - * struct rockchip_usb2phy: usb2.0 phy driver data. + * struct rockchip_usb2phy - usb2.0 phy driver data. + * @dev: pointer to device. * @grf: General Register Files regmap. * @usbgrf: USB General Register Files regmap. + * @usbctrl_grf: USB Controller General Register Files regmap. * *phy_base: the base address of USB PHY. + * @phy_reset: phy reset control. * @clks: array of phy input clocks. * @clk480m: clock struct of phy output clk. - * @clk_hw: clock struct of phy output clk management. + * @clk480m_hw: clock struct of phy output clk management. * @num_clks: number of phy input clocks. * @chg_state: states involved in USB charger detection. * @chg_type: USB charger types. @@ -306,7 +333,9 @@ struct device *dev; struct regmap *grf; struct regmap *usbgrf; + struct regmap *usbctrl_grf; void __iomem *phy_base; + struct reset_control *phy_reset; struct clk_bulk_data *clks; struct clk *clk480m; struct clk_hw clk480m_hw; @@ -376,6 +405,53 @@ orig = readl(base + reg->offset); tmp = (orig & mask) >> reg->bitstart; return tmp == reg->enable; +} + +static inline void phy_clear_bits(void __iomem *reg, u32 bits) +{ + u32 tmp = readl(reg); + + tmp &= ~bits; + writel(tmp, reg); +} + +static inline void phy_set_bits(void __iomem *reg, u32 bits) +{ + u32 tmp = readl(reg); + + tmp |= bits; + writel(tmp, reg); +} + +static inline void phy_update_bits(void __iomem *reg, u32 mask, u32 val) +{ + u32 tmp = readl(reg); + + tmp &= ~mask; + tmp |= val & mask; + writel(tmp, reg); +} + +static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy) +{ + int ret; + + if (!rphy->phy_reset) + return 0; + + ret = reset_control_assert(rphy->phy_reset); + if (ret) + return ret; + + udelay(10); + + ret = reset_control_deassert(rphy->phy_reset); + if (ret) + return ret; + + usleep_range(100, 200); + + return 0; } static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) @@ -595,6 +671,29 @@ return ret; } +static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + bool en) +{ + int ret; + + ret = property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, &rport->port_cfg->disfall_en, en); + if (ret) + goto out; + + ret = property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, &rport->port_cfg->disrise_en, en); +out: + return ret; +} + static int rockchip_usb_bypass_uart(struct rockchip_usb2phy_port *rport, bool en) { @@ -693,8 +792,13 @@ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); int ret = 0; + unsigned int ul, ul_mask; mutex_lock(&rport->mutex); + + if (rport->sel_pipe_phystatus) + property_enable(rphy->usbctrl_grf, + &rport->port_cfg->pipe_phystatus, true); if (rport->port_id == USB2PHY_PORT_OTG && (rport->mode == USB_DR_MODE_PERIPHERAL || @@ -721,11 +825,24 @@ "failed to enable bvalid irq\n"); goto out; } - schedule_delayed_work(&rport->otg_sm_work, - OTG_SCHEDULE_DELAY); + rport->typec_vbus_det ? 0 : OTG_SCHEDULE_DELAY); } } else if (rport->port_id == USB2PHY_PORT_HOST) { + if (rport->port_cfg->disfall_en.offset) { + ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul); + if (ret < 0) + goto out; + ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, + rport->port_cfg->utmi_ls.bitstart); + rport->host_disconnect = (ul & ul_mask) == 0 ? true : false; + ret = rockchip_usb2phy_enable_host_disc_irq(rphy, rport, true); + if (ret) { + dev_err(rphy->dev, "failed to enable disconnect irq\n"); + goto out; + } + } + /* clear linestate and enable linestate detect irq */ ret = rockchip_usb2phy_enable_line_irq(rphy, rport, true); if (ret) { @@ -773,6 +890,21 @@ ret = property_enable(base, &rport->port_cfg->phy_sus, false); if (ret) goto unlock; + + /* + * For rk3588, it needs to reset phy when exit from + * suspend mode with common_on_n 1'b1(aka REFCLK_LOGIC, + * Bias, and PLL blocks are powered down) for lower + * power consumption. If you don't want to reset phy, + * please keep the common_on_n 1'b0 to set these blocks + * remain powered. + */ + if (rport->port_id == USB2PHY_PORT_OTG && + of_device_is_compatible(rphy->dev->of_node, "rockchip,rk3588-usb2phy")) { + ret = rockchip_usb2phy_reset(rphy); + if (ret) + goto unlock; + } /* waiting for the utmi_clk to become stable */ usleep_range(1500, 2000); @@ -830,7 +962,7 @@ if (rport->port_id == USB2PHY_PORT_HOST) cancel_delayed_work_sync(&rport->sm_work); else if (rport->port_id == USB2PHY_PORT_OTG && - rport->bvalid_irq > 0) + rport->otg_sm_work.work.func) flush_delayed_work(&rport->otg_sm_work); return 0; @@ -859,7 +991,8 @@ return ret; } -static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode) +static int rockchip_usb2phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); @@ -871,16 +1004,31 @@ switch (mode) { case PHY_MODE_USB_OTG: + if (rphy->edev_self && submode) { + if (submode == USB_ROLE_HOST) { + extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); + extcon_set_state(rphy->edev, EXTCON_USB, false); + } else if (submode == USB_ROLE_DEVICE) { + extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); + extcon_set_state(rphy->edev, EXTCON_USB, true); + } + + return ret; + } + /* * In case of using vbus to detect connect state by u2phy, * enable vbus detect on otg mode. - * - * fallthrough */ + fallthrough; case PHY_MODE_USB_DEVICE: /* Disable VBUS supply */ rockchip_set_vbus_power(rport, false); extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, false); + /* For vbus always on, set EXTCON_USB to true. */ + if (rport->vbus_always_on) + extcon_set_state(rphy->edev, EXTCON_USB, true); + rport->perip_connected = true; vbus_det_en = true; break; case PHY_MODE_USB_HOST: @@ -893,7 +1041,11 @@ } extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, true); - /* fallthrough */ + /* For vbus always on, deinit EXTCON_USB to false. */ + if (rport->vbus_always_on) + extcon_set_state(rphy->edev, EXTCON_USB, false); + rport->perip_connected = false; + fallthrough; case PHY_MODE_INVALID: vbus_det_en = false; break; @@ -953,8 +1105,6 @@ return sprintf(buf, "otg\n"); case USB_DR_MODE_UNKNOWN: return sprintf(buf, "UNKNOWN\n"); - default: - break; } return -EINVAL; @@ -1011,17 +1161,17 @@ switch (rport->mode) { case USB_DR_MODE_HOST: - rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_HOST); + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_HOST, 0); property_enable(base, &rport->port_cfg->iddig_output, false); property_enable(base, &rport->port_cfg->iddig_en, true); break; case USB_DR_MODE_PERIPHERAL: - rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_DEVICE); + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_DEVICE, 0); property_enable(base, &rport->port_cfg->iddig_output, true); property_enable(base, &rport->port_cfg->iddig_en, true); break; case USB_DR_MODE_OTG: - rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_OTG); + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_OTG, 0); property_enable(base, &rport->port_cfg->iddig_output, false); property_enable(base, &rport->port_cfg->iddig_en, false); break; @@ -1060,7 +1210,10 @@ mutex_lock(&rport->mutex); - if (rport->utmi_avalid) + if (rport->port_cfg->bvalid_grf_con.enable && rport->typec_vbus_det) + rport->vbus_attached = + property_enabled(rphy->grf, &rport->port_cfg->bvalid_grf_con); + else if (rport->utmi_avalid) rport->vbus_attached = property_enabled(rphy->grf, &rport->port_cfg->utmi_avalid); else @@ -1082,10 +1235,10 @@ rockchip_usb2phy_power_off(rport->phy); mutex_lock(&rport->mutex); } - /* fall through */ + fallthrough; case OTG_STATE_B_IDLE: if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0 || - extcon_get_state(rphy->edev, EXTCON_USB_VBUS_EN) > 0 ) { + extcon_get_state(rphy->edev, EXTCON_USB_VBUS_EN) > 0) { dev_dbg(&rport->phy->dev, "usb otg host connect\n"); rport->state = OTG_STATE_A_HOST; rphy->chg_state = USB_CHG_STATE_UNDEFINED; @@ -1139,6 +1292,7 @@ } else { rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rport->perip_connected = false; mutex_unlock(&rport->mutex); if (!rport->dis_u2_susphy) rockchip_usb2phy_power_off(rport->phy); @@ -1370,25 +1524,27 @@ rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; else rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; - /* fall through */ + fallthrough; case USB_CHG_STATE_SECONDARY_DONE: rphy->chg_state = USB_CHG_STATE_DETECTED; - /* fall through */ + fallthrough; case USB_CHG_STATE_DETECTED: + if (rphy->phy_cfg->chg_det.chg_mode.offset != + rport->port_cfg->phy_sus.offset) + property_enable(base, &rphy->phy_cfg->chg_det.chg_mode, false); + /* Restore the PHY suspend configuration */ phy_sus_reg = &rport->port_cfg->phy_sus; mask = GENMASK(phy_sus_reg->bitend, phy_sus_reg->bitstart); ret = regmap_write(base, phy_sus_reg->offset, - ((rphy->phy_sus_cfg << - phy_sus_reg->bitstart) | - (mask << BIT_WRITEABLE_SHIFT))); + (rphy->phy_sus_cfg | (mask << BIT_WRITEABLE_SHIFT))); if (ret) dev_err(&rport->phy->dev, "Fail to set phy_sus reg offset 0x%x, ret %d\n", phy_sus_reg->offset, ret); mutex_unlock(&rport->mutex); rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); - dev_info(&rport->phy->dev, "charger = %s\n", + dev_dbg(&rport->phy->dev, "charger = %s\n", chg_to_string(rphy->chg_type)); return; default: @@ -1422,14 +1578,13 @@ struct rockchip_usb2phy_port *rport = container_of(work, struct rockchip_usb2phy_port, sm_work.work); struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - - rport->port_cfg->utmi_hstdet.bitstart + 1; - unsigned int ul, uhd, state; + unsigned int sh, ul, uhd, state; unsigned int ul_mask, uhd_mask; int ret; if (!rport->port_cfg->utmi_ls.offset || - !rport->port_cfg->utmi_hstdet.offset) { + (!rport->port_cfg->utmi_hstdet.offset && + !rport->port_cfg->disfall_en.offset)) { dev_dbg(&rport->phy->dev, "some property may not be specified\n"); return; } @@ -1440,18 +1595,26 @@ if (ret < 0) goto next_schedule; - ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd); - if (ret < 0) - goto next_schedule; - - uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, - rport->port_cfg->utmi_hstdet.bitstart); ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, rport->port_cfg->utmi_ls.bitstart); - /* stitch on utmi_ls and utmi_hstdet as phy state */ - state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | - (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); + if (rport->port_cfg->utmi_hstdet.offset) { + ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd); + if (ret < 0) + goto next_schedule; + + uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, + rport->port_cfg->utmi_hstdet.bitstart); + + sh = rport->port_cfg->utmi_hstdet.bitend - + rport->port_cfg->utmi_hstdet.bitstart + 1; + /* stitch on utmi_ls and utmi_hstdet as phy state */ + state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | + (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); + } else { + state = ((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << 1 | + rport->host_disconnect; + } switch (state) { case PHY_STATE_HS_ONLINE: @@ -1472,7 +1635,7 @@ dev_dbg(&rport->phy->dev, "FS/LS online\n"); break; } - /* fall through */ + fallthrough; case PHY_STATE_CONNECT: if (rport->suspended) { dev_dbg(&rport->phy->dev, "Connected\n"); @@ -1516,7 +1679,7 @@ mutex_unlock(&rport->mutex); return; default: - dev_dbg(&rport->phy->dev, "unknown phy state\n"); + dev_dbg(&rport->phy->dev, "unknown phy state %d\n", state); break; } @@ -1530,7 +1693,8 @@ struct rockchip_usb2phy_port *rport = data; struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - if (!property_enabled(rphy->grf, &rport->port_cfg->ls_det_st)) + if (!property_enabled(rphy->grf, &rport->port_cfg->ls_det_st) || + !property_enabled(rphy->grf, &rport->port_cfg->ls_det_en)) return IRQ_NONE; dev_dbg(&rport->phy->dev, "linestate interrupt\n"); @@ -1539,6 +1703,14 @@ /* disable linestate detect irq and clear its status */ rockchip_usb2phy_enable_line_irq(rphy, rport, false); + + /* + * For host port, it may miss disc irq when device is connected, + * in this case, we can clear host_disconnect state depend on + * the linestate irq. + */ + if (rport->port_id == USB2PHY_PORT_HOST && rport->port_cfg->disfall_en.offset) + rport->host_disconnect = false; mutex_unlock(&rport->mutex); @@ -1571,8 +1743,10 @@ if (rport->bypass_uart_en) rockchip_usb_bypass_uart(rport, false); - cancel_delayed_work_sync(&rport->otg_sm_work); - rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + if (rport->otg_sm_work.work.func) { + cancel_delayed_work_sync(&rport->otg_sm_work); + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + } return IRQ_HANDLED; } @@ -1593,7 +1767,9 @@ if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) { property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr, true); - cable_vbus_state = true; + /* switch to host if id fall det and iddig status is low */ + if (!property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig)) + cable_vbus_state = true; } else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) { property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr, true); @@ -1607,6 +1783,33 @@ extcon_sync(rphy->edev, EXTCON_USB_VBUS_EN); rockchip_set_vbus_power(rport, cable_vbus_state); + + mutex_unlock(&rport->mutex); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_usb2phy_host_disc_irq(int irq, void *data) +{ + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + + if (!property_enabled(rphy->grf, &rport->port_cfg->disfall_st) && + !property_enabled(rphy->grf, &rport->port_cfg->disrise_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* clear disconnect fall or rise detect irq pending status */ + if (property_enabled(rphy->grf, &rport->port_cfg->disfall_st)) { + property_enable(rphy->grf, &rport->port_cfg->disfall_clr, + true); + rport->host_disconnect = false; + } else if (property_enabled(rphy->grf, &rport->port_cfg->disrise_st)) { + property_enable(rphy->grf, &rport->port_cfg->disrise_clr, + true); + rport->host_disconnect = true; + } mutex_unlock(&rport->mutex); @@ -1637,8 +1840,16 @@ if (!rport->phy) continue; + /* + * Handle disc irq before linestate irq to set the disc + * state for sm work scheduled in the linestate irq handler. + */ + if (rport->port_id == USB2PHY_PORT_HOST && + rport->port_cfg->disfall_en.offset) + ret |= rockchip_usb2phy_host_disc_irq(irq, rport); + /* Handle linestate irq for both otg port and host port */ - ret = rockchip_usb2phy_linestate_irq(irq, rport); + ret |= rockchip_usb2phy_linestate_irq(irq, rport); /* * Handle bvalid irq and id irq for otg port which @@ -1777,6 +1988,61 @@ return ret; } +static void rockchip_usb2phy_usb_bvalid_enable(struct rockchip_usb2phy_port *rport, + u8 enable) +{ + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + const struct rockchip_usb2phy_port_cfg *cfg = rport->port_cfg; + + if (cfg->bvalid_phy_con.enable) + property_enable(rphy->grf, &cfg->bvalid_phy_con, enable); + + if (cfg->bvalid_grf_con.enable) + property_enable(rphy->grf, &cfg->bvalid_grf_con, enable); +} + +static int rockchip_usb2phy_orien_sw_set(struct typec_switch *sw, + enum typec_orientation orien) +{ + struct rockchip_usb2phy_port *rport = typec_switch_get_drvdata(sw); + + dev_dbg(&rport->phy->dev, "type-c orientation: %d\n", orien); + + mutex_lock(&rport->mutex); + rockchip_usb2phy_usb_bvalid_enable(rport, orien != TYPEC_ORIENTATION_NONE); + mutex_unlock(&rport->mutex); + + return 0; +} + +static int +rockchip_usb2phy_setup_orien_switch(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport) +{ + struct typec_switch_desc sw_desc = { }; + struct device *dev = rphy->dev; + + sw_desc.drvdata = rport; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = rockchip_usb2phy_orien_sw_set; + + rport->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(rport->sw)) { + dev_err(dev, "Error register typec orientation switch: %ld\n", + PTR_ERR(rport->sw)); + return PTR_ERR(rport->sw); + } + + return 0; +} + +static void rockchip_usb2phy_orien_switch_unregister(void *data) +{ + struct rockchip_usb2phy_port *rport = data; + + typec_switch_unregister(rport->sw); +} + static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) @@ -1861,6 +2127,23 @@ rport->low_power_en = of_property_read_bool(child_np, "rockchip,low-power-mode"); + /* For type-c with vbus_det always pull up */ + rport->typec_vbus_det = + of_property_read_bool(child_np, "rockchip,typec-vbus-det"); + + rport->sel_pipe_phystatus = + of_property_read_bool(child_np, "rockchip,sel-pipe-phystatus"); + + if (rport->sel_pipe_phystatus) { + rphy->usbctrl_grf = + syscon_regmap_lookup_by_phandle(rphy->dev->of_node, + "rockchip,usbctrl-grf"); + if (IS_ERR(rphy->usbctrl_grf)) { + dev_err(rphy->dev, "Failed to map usbctrl-grf\n"); + return PTR_ERR(rphy->usbctrl_grf); + } + } + /* Get Vbus regulators */ rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus"); if (IS_ERR(rport->vbus)) { @@ -1868,7 +2151,8 @@ if (ret == -EPROBE_DEFER) return ret; - dev_warn(&rport->phy->dev, "No vbus specified for otg port\n"); + if (rport->mode == USB_DR_MODE_OTG) + dev_warn(&rport->phy->dev, "No vbus specified for otg port\n"); rport->vbus = NULL; } @@ -1891,13 +2175,42 @@ return ret; } + if (IS_REACHABLE(CONFIG_TYPEC) && + device_property_present(rphy->dev, "orientation-switch")) { + ret = rockchip_usb2phy_setup_orien_switch(rphy, rport); + if (ret) + return ret; + + ret = devm_add_action_or_reset(rphy->dev, + rockchip_usb2phy_orien_switch_unregister, + rport); + if (ret) + return ret; + } + + /* + * Set the utmi bvalid come from the usb phy or grf. + * For most of Rockchip SoCs, them have VBUSDET pin + * for the usb phy to detect the USB VBUS and set + * the bvalid signal, so select the bvalid from the + * usb phy by default. And for those SoCs which don't + * have VBUSDET pin (e.g. RV1103), it needs to select + * the bvaid from the grf and set bvalid to be valid + * (high) by default. + */ + if (rport->port_cfg->bvalid_grf_sel.enable != 0) { + if (of_machine_is_compatible("rockchip,rv1103")) + property_enable(base, &rport->port_cfg->bvalid_grf_sel, true); + else + property_enable(base, &rport->port_cfg->bvalid_grf_sel, false); + } + + if (rport->vbus_always_on) + extcon_set_state(rphy->edev, EXTCON_USB, true); + if (rport->vbus_always_on || rport->mode == USB_DR_MODE_HOST || rport->mode == USB_DR_MODE_UNKNOWN) goto out; - - /* Select bvalid of usb phy as bvalid of usb controller */ - if (rport->port_cfg->bvalid_set.enable != 0) - property_enable(base, &rport->port_cfg->bvalid_set, false); wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg"); ret = devm_add_action_or_reset(rphy->dev, rockchip_otg_wake_lock_destroy, @@ -2012,24 +2325,29 @@ /* find out a proper config which can be matched with dt. */ index = 0; - while (phy_cfgs[index].reg) { + do { if (phy_cfgs[index].reg == reg) { rphy->phy_cfg = &phy_cfgs[index]; break; } ++index; - } + } while (phy_cfgs[index].reg); if (!rphy->phy_cfg) { - dev_err(dev, "no phy-config can be matched with %s node\n", - np->name); + dev_err(dev, "no phy-config can be matched with %pOFn node\n", + np); return -EINVAL; } pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_get_sync(dev); + + + rphy->phy_reset = devm_reset_control_get_optional(dev, "phy"); + if (IS_ERR(rphy->phy_reset)) + return PTR_ERR(rphy->phy_reset); ret = devm_clk_bulk_get_all(dev, &rphy->clks); if (ret == -EPROBE_DEFER) @@ -2057,8 +2375,8 @@ struct phy *phy; /* This driver aims to support both otg-port and host-port */ - if (of_node_cmp(child_np->name, "host-port") && - of_node_cmp(child_np->name, "otg-port")) + if (!of_node_name_eq(child_np, "host-port") && + !of_node_name_eq(child_np, "otg-port")) goto next_child; phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); @@ -2072,7 +2390,7 @@ phy_set_drvdata(rport->phy, rport); /* initialize otg/host port separately */ - if (!of_node_cmp(child_np->name, "host-port")) { + if (of_node_name_eq(child_np, "host-port")) { ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np); if (ret) @@ -2136,7 +2454,6 @@ pm_runtime_put_sync(dev); pm_runtime_disable(dev); clk_bulk_disable_unprepare(rphy->num_clks, rphy->clks); - return ret; } @@ -2203,20 +2520,55 @@ { int ret; - /* Open pre-emphasize in non-chirp state for otg port */ - ret = regmap_write(rphy->grf, 0x0, 0x00070004); - if (ret) - return ret; + if (soc_is_rk3308bs()) { + /* Turn off differential receiver in suspend mode */ + ret = regmap_update_bits(rphy->grf, 0x30, BIT(2), 0); + if (ret) + return ret; - /* Open pre-emphasize in non-chirp state for host port */ - ret = regmap_write(rphy->grf, 0x30, 0x00070004); - if (ret) - return ret; + /* Enable otg port pre-emphasis during non-chirp phase */ + ret = regmap_update_bits(rphy->grf, 0, GENMASK(2, 0), BIT(2)); + if (ret) + return ret; - /* Turn off differential receiver in suspend mode */ - ret = regmap_write(rphy->grf, 0x18, 0x00040000); - if (ret) - return ret; + /* Set otg port squelch trigger point configure to 100mv */ + ret = regmap_update_bits(rphy->grf, 0x004, GENMASK(7, 5), 0x40); + if (ret) + return ret; + + ret = regmap_update_bits(rphy->grf, 0x008, BIT(0), 0x1); + if (ret) + return ret; + + /* Enable host port pre-emphasis during non-chirp phase */ + ret = regmap_update_bits(rphy->grf, 0x400, GENMASK(2, 0), BIT(2)); + if (ret) + return ret; + + /* Set host port squelch trigger point configure to 100mv */ + ret = regmap_update_bits(rphy->grf, 0x404, GENMASK(7, 5), 0x40); + if (ret) + return ret; + + ret = regmap_update_bits(rphy->grf, 0x408, BIT(0), 0x1); + if (ret) + return ret; + } else { + /* Open pre-emphasize in non-chirp state for otg port */ + ret = regmap_write(rphy->grf, 0x0, 0x00070004); + if (ret) + return ret; + + /* Open pre-emphasize in non-chirp state for host port */ + ret = regmap_write(rphy->grf, 0x30, 0x00070004); + if (ret) + return ret; + + /* Turn off differential receiver in suspend mode */ + ret = regmap_write(rphy->grf, 0x18, 0x00040000); + if (ret) + return ret; + } return 0; } @@ -2379,34 +2731,22 @@ static int rk3528_usb2phy_tuning(struct rockchip_usb2phy *rphy) { - u32 reg; int ret = 0; /* Turn off otg port differential receiver in suspend mode */ - reg = readl(rphy->phy_base + 0x30); - writel(reg & ~BIT(2), rphy->phy_base + 0x30); + phy_clear_bits(rphy->phy_base + 0x30, BIT(2)); /* Turn off host port differential receiver in suspend mode */ - reg = readl(rphy->phy_base + 0x0430); - writel(reg & ~BIT(2), rphy->phy_base + 0x0430); + phy_clear_bits(rphy->phy_base + 0x430, BIT(2)); /* Set otg port HS eye height to 400mv(default is 450mv) */ - reg = readl(rphy->phy_base + 0x30); - reg &= ~GENMASK(6, 4); - reg |= (0x00 << 4); - writel(reg, rphy->phy_base + 0x30); + phy_update_bits(rphy->phy_base + 0x30, GENMASK(6, 4), (0x00 << 4)); /* Set host port HS eye height to 400mv(default is 450mv) */ - reg = readl(rphy->phy_base + 0x430); - reg &= ~GENMASK(6, 4); - reg |= (0x00 << 4); - writel(reg, rphy->phy_base + 0x430); + phy_update_bits(rphy->phy_base + 0x430, GENMASK(6, 4), (0x00 << 4)); /* Choose the Tx fs/ls data as linestate from TX driver for otg port */ - reg = readl(rphy->phy_base + 0x94); - reg &= ~GENMASK(6, 3); - reg |= (0x03 << 3); - writel(reg, rphy->phy_base + 0x94); + phy_update_bits(rphy->phy_base + 0x94, GENMASK(6, 3), (0x03 << 3)); /* Enable otg and host ports phy irq to pmu wakeup source */ ret |= regmap_write(rphy->grf, 0x80004, 0x00030003); @@ -2414,33 +2754,50 @@ return ret; } -static int rk3568_usb2phy_tuning(struct rockchip_usb2phy *rphy) +static int rk3562_usb2phy_tuning(struct rockchip_usb2phy *rphy) { - u32 reg; int ret = 0; - reg = readl(rphy->phy_base + 0x30); - /* turn off differential reciver in suspend mode */ - writel(reg & ~BIT(2), rphy->phy_base + 0x30); + /* Turn off differential receiver by default to save power */ + phy_clear_bits(rphy->phy_base + 0x0030, BIT(2)); + phy_clear_bits(rphy->phy_base + 0x0430, BIT(2)); - reg = readl(rphy->phy_base); + /* Enable pre-emphasis during non-chirp phase */ + phy_update_bits(rphy->phy_base, GENMASK(2, 0), 0x04); + phy_update_bits(rphy->phy_base + 0x0400, GENMASK(2, 0), 0x04); + + /* Set HS eye height to 425mv(default is 400mv) */ + phy_update_bits(rphy->phy_base + 0x0030, GENMASK(6, 4), (0x05 << 4)); + phy_update_bits(rphy->phy_base + 0x0430, GENMASK(6, 4), (0x05 << 4)); + + /* Set the bvalid filter time to 10ms based on the u2phy grf pclk 100MHz */ + ret |= regmap_write(rphy->grf, 0x0138, FILTER_COUNTER); + + /* Set the id filter time to 10ms based on the u2phy grf pclk 100MHz */ + ret |= regmap_write(rphy->grf, 0x013c, FILTER_COUNTER); + + /* Enable host port wakeup irq */ + ret |= regmap_write(rphy->grf, 0x010c, 0x80008000); + + return ret; +} + +static int rk3568_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + int ret = 0; + + /* Turn off differential receiver by default to save power */ + phy_clear_bits(rphy->phy_base + 0x30, BIT(2)); + /* Enable otg port pre-emphasis during non-chirp phase */ - reg &= ~(0x07 << 0); - reg |= (0x04 << 0); - writel(reg, rphy->phy_base); + phy_update_bits(rphy->phy_base, GENMASK(2, 0), 0x04); - reg = readl(rphy->phy_base + 0x0400); /* Enable host port pre-emphasis during non-chirp phase */ - reg &= ~(0x07 << 0); - reg |= (0x04 << 0); - writel(reg, rphy->phy_base + 0x0400); + phy_update_bits(rphy->phy_base + 0x0400, GENMASK(2, 0), 0x04); if (rphy->phy_cfg->reg == 0xfe8a0000) { /* Set otg port HS eye height to 437.5mv(default is 400mv) */ - reg = readl(rphy->phy_base + 0x30); - reg &= ~(0x07 << 4); - reg |= (0x06 << 4); - writel(reg, rphy->phy_base + 0x30); + phy_update_bits(rphy->phy_base + 0x30, GENMASK(6, 4), (0x06 << 4)); /* * Set the bvalid filter time to 10ms @@ -2461,23 +2818,158 @@ return ret; } +static int rv1106_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + /* Always enable pre-emphasis in SOF & EOP & chirp & non-chirp state */ + phy_update_bits(rphy->phy_base + 0x30, GENMASK(2, 0), 0x07); + + if (rockchip_get_cpu_version()) { + /* Set Tx HS pre_emphasize strength to 3'b001 */ + phy_update_bits(rphy->phy_base + 0x40, GENMASK(5, 3), (0x01 << 3)); + } else { + /* Set Tx HS pre_emphasize strength to 3'b011 */ + phy_update_bits(rphy->phy_base + 0x40, GENMASK(5, 3), (0x03 << 3)); + } + + /* Set RX Squelch trigger point configure to 4'b0000(112.5 mV) */ + phy_update_bits(rphy->phy_base + 0x64, GENMASK(6, 3), (0x00 << 3)); + + /* Turn off differential receiver by default to save power */ + phy_clear_bits(rphy->phy_base + 0x100, BIT(6)); + + /* Set 45ohm HS ODT value to 5'b10111 to increase driver strength */ + phy_update_bits(rphy->phy_base + 0x11c, GENMASK(4, 0), 0x17); + + /* Set Tx HS eye height tuning to 3'b011(462 mV)*/ + phy_update_bits(rphy->phy_base + 0x124, GENMASK(4, 2), (0x03 << 2)); + + /* Bypass Squelch detector calibration */ + phy_update_bits(rphy->phy_base + 0x1a4, GENMASK(7, 4), (0x01 << 4)); + phy_update_bits(rphy->phy_base + 0x1b4, GENMASK(7, 4), (0x01 << 4)); + + /* Set HS disconnect detect mode to single ended detect mode */ + phy_set_bits(rphy->phy_base + 0x70, BIT(2)); + + return 0; +} + static int rockchip_usb2phy_vbus_det_control(struct rockchip_usb2phy *rphy, const struct usb2phy_reg *vbus_det_en, bool en) { - u32 reg; - if (en) { - reg = readl(rphy->phy_base + vbus_det_en->offset); /* Enable vbus voltage level detection function */ - writel(reg & ~BIT(7), rphy->phy_base + vbus_det_en->offset); + phy_clear_bits(rphy->phy_base + vbus_det_en->offset, BIT(7)); } else { - reg = readl(rphy->phy_base + vbus_det_en->offset); /* Disable vbus voltage level detection function */ - writel(reg | BIT(7), rphy->phy_base + vbus_det_en->offset); + phy_set_bits(rphy->phy_base + vbus_det_en->offset, BIT(7)); } return 0; +} + +static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + unsigned int reg; + int ret = 0; + + /* Read the SIDDQ control register */ + ret = regmap_read(rphy->grf, 0x0008, ®); + if (ret) + return ret; + + if (reg & BIT(13)) { + /* Deassert SIDDQ to power on analog block */ + ret = regmap_write(rphy->grf, 0x0008, + GENMASK(29, 29) | 0x0000); + if (ret) + return ret; + + /* Do reset after exit IDDQ mode */ + ret = rockchip_usb2phy_reset(rphy); + if (ret) + return ret; + } + + if (rphy->phy_cfg->reg == 0x0000) { + /* + * Set USB2 PHY0 suspend configuration for USB3_0 + * 1. Set utmi_termselect to 1'b1 (en FS terminations) + * 2. Set utmi_xcvrselect to 2'b01 (FS transceiver) + * 3. Set utmi_opmode to 2'b01 (no-driving) + */ + ret |= regmap_write(rphy->grf, 0x000c, + GENMASK(20, 16) | 0x0015); + + /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ + ret |= regmap_write(rphy->grf, 0x0004, + GENMASK(27, 24) | 0x0900); + + /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ + ret |= regmap_write(rphy->grf, 0x0008, + GENMASK(20, 19) | 0x0010); + + /* Pullup iddig pin for USB3_0 OTG mode */ + ret |= regmap_write(rphy->grf, 0x0010, + GENMASK(17, 16) | 0x0003); + } else if (rphy->phy_cfg->reg == 0x4000) { + /* + * Set USB2 PHY1 suspend configuration for USB3_1 + * 1. Set utmi_termselect to 1'b1 (en FS terminations) + * 2. Set utmi_xcvrselect to 2'b01(FS transceiver) + * 3. Set utmi_opmode to 2'b01 (no-driving) + */ + ret |= regmap_write(rphy->grf, 0x000c, + GENMASK(20, 16) | 0x0015); + + /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ + ret |= regmap_write(rphy->grf, 0x0004, + GENMASK(27, 24) | 0x0900); + + /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ + ret |= regmap_write(rphy->grf, 0x0008, + GENMASK(20, 19) | 0x0010); + + /* Pullup iddig pin for USB3_1 OTG mode */ + ret |= regmap_write(rphy->grf, 0x0010, + GENMASK(17, 16) | 0x0003); + } else if (rphy->phy_cfg->reg == 0x8000) { + /* + * Set USB2 PHY2 suspend configuration for USB2_0 + * 1. Set utmi_termselect to 1'b1 (en FS terminations) + * 2. Set utmi_xcvrselect to 2'b01(FS transceiver) + * 3. Set utmi_opmode to 2'b00 (normal) + */ + ret |= regmap_write(rphy->grf, 0x000c, + GENMASK(20, 16) | 0x0014); + + /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ + ret |= regmap_write(rphy->grf, 0x0004, + GENMASK(27, 24) | 0x0900); + + /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ + ret |= regmap_write(rphy->grf, 0x0008, + GENMASK(20, 19) | 0x0010); + } else if (rphy->phy_cfg->reg == 0xc000) { + /* + * Set USB2 PHY3 suspend configuration for USB2_1 + * 1. Set utmi_termselect to 1'b1 (en FS terminations) + * 2. Set utmi_xcvrselect to 2'b01(FS transceiver) + * 3. Set utmi_opmode to 2'b00 (normal) + */ + ret |= regmap_write(rphy->grf, 0x000c, + GENMASK(20, 16) | 0x0014); + + /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ + ret |= regmap_write(rphy->grf, 0x0004, + GENMASK(27, 24) | 0x0900); + + /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ + ret |= regmap_write(rphy->grf, 0x0008, + GENMASK(20, 19) | 0x0010); + } + + return ret; } #ifdef CONFIG_PM_SLEEP @@ -2570,6 +3062,13 @@ if (device_may_wakeup(rphy->dev)) wakeup_enable = true; + + /* + * PHY lost power in suspend, it needs to reset + * PHY to recovery clock to usb controller. + */ + if (!wakeup_enable) + rockchip_usb2phy_reset(rphy); if (phy_cfg->phy_tuning) ret = phy_cfg->phy_tuning(rphy); @@ -2987,7 +3486,7 @@ .vbus_det_en = { 0x079c, 15, 15, 1, 0 }, }, [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x0728, 8, 0, 0, 0x1d1 }, + .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, .ls_det_en = { 0x0680, 4, 4, 0, 1 }, .ls_det_st = { 0x0690, 4, 4, 0, 1 }, .ls_det_clr = { 0x06a0, 4, 4, 0, 1 } @@ -3128,7 +3627,6 @@ .bvalid_det_en = { 0x60074, 2, 2, 0, 1 }, .bvalid_det_st = { 0x60078, 2, 2, 0, 1 }, .bvalid_det_clr = { 0x6007c, 2, 2, 0, 1 }, - .bvalid_set = { 0x6004c, 15, 14, 0, 3 }, .iddig_output = { 0x6004c, 10, 10, 0, 1 }, .iddig_en = { 0x6004c, 9, 9, 0, 1 }, .idfall_det_en = { 0x60074, 5, 5, 0, 1 }, @@ -3172,6 +3670,65 @@ } }; +static const struct rockchip_usb2phy_cfg rk3562_phy_cfgs[] = { + { + .reg = 0xff740000, + .num_ports = 2, + .phy_tuning = rk3562_usb2phy_tuning, + .vbus_detect = rockchip_usb2phy_vbus_det_control, + .clkout_ctl = { 0x0108, 4, 4, 1, 0 }, + .ls_filter_con = { 0x0130, 19, 0, 0x30100, 0x00020 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, + .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, + .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, + .bvalid_grf_sel = { 0x0108, 15, 14, 0, 3 }, + .bypass_dm_en = { 0x0108, 2, 2, 0, 1}, + .bypass_sel = { 0x0108, 3, 3, 0, 1}, + .iddig_output = { 0x0100, 10, 10, 0, 1 }, + .iddig_en = { 0x0100, 9, 9, 0, 1 }, + .idfall_det_en = { 0x0110, 5, 5, 0, 1 }, + .idfall_det_st = { 0x0114, 5, 5, 0, 1 }, + .idfall_det_clr = { 0x0118, 5, 5, 0, 1 }, + .idrise_det_en = { 0x0110, 4, 4, 0, 1 }, + .idrise_det_st = { 0x0114, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x0118, 4, 4, 0, 1 }, + .ls_det_en = { 0x0110, 0, 0, 0, 1 }, + .ls_det_st = { 0x0114, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, + .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, + .utmi_iddig = { 0x0120, 6, 6, 0, 1 }, + .utmi_ls = { 0x0120, 5, 4, 0, 1 }, + .vbus_det_en = { 0x003c, 7, 7, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0104, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0110, 1, 1, 0, 1 }, + .ls_det_st = { 0x0114, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0118, 1, 1, 0, 1 }, + .utmi_ls = { 0x0120, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x0120, 19, 19, 0, 1 } + } + }, + .chg_det = { + .chg_mode = { 0x0100, 8, 0, 0, 0x1d7 }, + .cp_det = { 0x0120, 24, 24, 0, 1 }, + .dcp_det = { 0x0120, 23, 23, 0, 1 }, + .dp_det = { 0x0120, 25, 25, 0, 1 }, + .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, + .idp_src_en = { 0x0108, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { { .reg = 0xfe8a0000, @@ -3186,7 +3743,7 @@ .bvalid_det_en = { 0x0080, 2, 2, 0, 1 }, .bvalid_det_st = { 0x0084, 2, 2, 0, 1 }, .bvalid_det_clr = { 0x0088, 2, 2, 0, 1 }, - .bvalid_set = { 0x0008, 15, 14, 0, 3 }, + .bvalid_grf_sel = { 0x0008, 15, 14, 0, 3 }, .bypass_dm_en = { 0x0008, 2, 2, 0, 1}, .bypass_sel = { 0x0008, 3, 3, 0, 1}, .iddig_output = { 0x0000, 10, 10, 0, 1 }, @@ -3257,6 +3814,208 @@ { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = { + { + .reg = 0x0000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 1, 0 }, + .ls_filter_con = { 0x0040, 19, 0, 0x30100, 0x00020 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x000c, 11, 11, 0, 1 }, + .pipe_phystatus = { 0x001c, 3, 2, 0, 2 }, + .bvalid_det_en = { 0x0080, 1, 1, 0, 1 }, + .bvalid_det_st = { 0x0084, 1, 1, 0, 1 }, + .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 }, + .bvalid_grf_sel = { 0x0010, 3, 3, 0, 1 }, + .bvalid_grf_con = { 0x0010, 3, 2, 2, 3 }, + .bvalid_phy_con = { 0x0008, 1, 0, 2, 3 }, + .bypass_dm_en = { 0x000c, 5, 5, 0, 1 }, + .bypass_sel = { 0x000c, 6, 6, 0, 1 }, + .iddig_output = { 0x0010, 0, 0, 0, 1 }, + .iddig_en = { 0x0010, 1, 1, 0, 1 }, + .idfall_det_en = { 0x0080, 4, 4, 0, 1 }, + .idfall_det_st = { 0x0084, 4, 4, 0, 1 }, + .idfall_det_clr = { 0x0088, 4, 4, 0, 1 }, + .idrise_det_en = { 0x0080, 3, 3, 0, 1 }, + .idrise_det_st = { 0x0084, 3, 3, 0, 1 }, + .idrise_det_clr = { 0x0088, 3, 3, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_avalid = { 0x00c0, 7, 7, 0, 1 }, + .utmi_bvalid = { 0x00c0, 6, 6, 0, 1 }, + .utmi_iddig = { 0x00c0, 5, 5, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + .chg_det = { + .chg_mode = { 0x0008, 2, 2, 0, 1 }, + .cp_det = { 0x00c0, 0, 0, 0, 1 }, + .dcp_det = { 0x00c0, 0, 0, 0, 1 }, + .dp_det = { 0x00c0, 1, 1, 1, 0 }, + .idm_sink_en = { 0x0008, 5, 5, 1, 0 }, + .idp_sink_en = { 0x0008, 5, 5, 0, 1 }, + .idp_src_en = { 0x0008, 14, 14, 0, 1 }, + .rdm_pdwn_en = { 0x0008, 14, 14, 0, 1 }, + .vdm_src_en = { 0x0008, 7, 6, 0, 3 }, + .vdp_src_en = { 0x0008, 7, 6, 0, 3 }, + }, + }, + { + .reg = 0x4000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 1, 0 }, + .ls_filter_con = { 0x0040, 19, 0, 0x30100, 0x00020 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x000c, 11, 11, 0, 1 }, + .pipe_phystatus = { 0x0034, 3, 2, 0, 2 }, + .bvalid_det_en = { 0x0080, 1, 1, 0, 1 }, + .bvalid_det_st = { 0x0084, 1, 1, 0, 1 }, + .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 }, + .bvalid_grf_sel = { 0x0010, 3, 3, 0, 1 }, + .bvalid_grf_con = { 0x0010, 3, 2, 2, 3 }, + .bvalid_phy_con = { 0x0008, 1, 0, 2, 3 }, + .bypass_dm_en = { 0x000c, 5, 5, 0, 1 }, + .bypass_sel = { 0x000c, 6, 6, 0, 1 }, + .iddig_output = { 0x0010, 0, 0, 0, 1 }, + .iddig_en = { 0x0010, 1, 1, 0, 1 }, + .idfall_det_en = { 0x0080, 4, 4, 0, 1 }, + .idfall_det_st = { 0x0084, 4, 4, 0, 1 }, + .idfall_det_clr = { 0x0088, 4, 4, 0, 1 }, + .idrise_det_en = { 0x0080, 3, 3, 0, 1 }, + .idrise_det_st = { 0x0084, 3, 3, 0, 1 }, + .idrise_det_clr = { 0x0088, 3, 3, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_avalid = { 0x00c0, 7, 7, 0, 1 }, + .utmi_bvalid = { 0x00c0, 6, 6, 0, 1 }, + .utmi_iddig = { 0x00c0, 5, 5, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + .chg_det = { + .chg_mode = { 0x0008, 2, 2, 0, 1 }, + .cp_det = { 0x00c0, 0, 0, 0, 1 }, + .dcp_det = { 0x00c0, 0, 0, 0, 1 }, + .dp_det = { 0x00c0, 1, 1, 1, 0 }, + .idm_sink_en = { 0x0008, 5, 5, 1, 0 }, + .idp_sink_en = { 0x0008, 5, 5, 0, 1 }, + .idp_src_en = { 0x0008, 14, 14, 0, 1 }, + .rdm_pdwn_en = { 0x0008, 14, 14, 0, 1 }, + .vdm_src_en = { 0x0008, 7, 6, 0, 3 }, + .vdp_src_en = { 0x0008, 7, 6, 0, 3 }, + }, + }, + { + .reg = 0x8000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 0, 0 }, + .ls_filter_con = { 0x0040, 19, 0, 0x30100, 0x00020 }, + .port_cfgs = { + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0008, 2, 2, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + }, + { + .reg = 0xc000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 0, 0 }, + .ls_filter_con = { 0x0040, 19, 0, 0x30100, 0x00020 }, + .port_cfgs = { + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0008, 2, 2, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + }, + { /* sentinel */ } +}; + +static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = { + { + .reg = 0xff3e0000, + .num_ports = 1, + .phy_tuning = rv1106_usb2phy_tuning, + .clkout_ctl = { 0x0058, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0050, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x0100, 2, 2, 0, 1 }, + .bvalid_det_st = { 0x0104, 2, 2, 0, 1 }, + .bvalid_det_clr = { 0x0108, 2, 2, 0, 1 }, + .bvalid_grf_sel = { 0x0058, 15, 14, 0, 3 }, + .iddig_output = { 0x0050, 10, 10, 0, 1 }, + .iddig_en = { 0x0050, 9, 9, 0, 1 }, + .idfall_det_en = { 0x0100, 5, 5, 0, 1 }, + .idfall_det_st = { 0x0104, 5, 5, 0, 1 }, + .idfall_det_clr = { 0x0108, 5, 5, 0, 1 }, + .idrise_det_en = { 0x0100, 4, 4, 0, 1 }, + .idrise_det_st = { 0x0104, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x0108, 4, 4, 0, 1 }, + .ls_det_en = { 0x0100, 0, 0, 0, 1 }, + .ls_det_st = { 0x0104, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0108, 0, 0, 0, 1 }, + .utmi_avalid = { 0x0060, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x0060, 9, 9, 0, 1 }, + .utmi_iddig = { 0x0060, 6, 6, 0, 1 }, + .utmi_ls = { 0x0060, 5, 4, 0, 1 }, + }, + }, + .chg_det = { + .chg_mode = { 0x0050, 8, 0, 0, 0x1d7 }, + .cp_det = { 0x0060, 13, 13, 0, 1 }, + .dcp_det = { 0x0060, 12, 12, 0, 1 }, + .dp_det = { 0x0060, 14, 14, 0, 1 }, + .idm_sink_en = { 0x0058, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0058, 7, 7, 0, 1 }, + .idp_src_en = { 0x0058, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0058, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0058, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0058, 11, 11, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { { .reg = 0x100, @@ -3300,17 +4059,51 @@ }; static const struct of_device_id rockchip_usb2phy_dt_match[] = { +#ifdef CONFIG_CPU_PX30 + { .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK1808 { .compatible = "rockchip,rk1808-usb2phy", .data = &rk1808_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK312X { .compatible = "rockchip,rk3128-usb2phy", .data = &rk312x_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK322X { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3308 { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3328 { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3366 { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3368 { .compatible = "rockchip,rk3368-usb2phy", .data = &rk3368_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3399 { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3528 { .compatible = "rockchip,rk3528-usb2phy", .data = &rk3528_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3562 + { .compatible = "rockchip,rk3562-usb2phy", .data = &rk3562_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3568 { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RK3588 + { .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RV1106 + { .compatible = "rockchip,rv1106-usb2phy", .data = &rv1106_phy_cfgs }, +#endif +#ifdef CONFIG_CPU_RV1108 { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, +#endif {} }; MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); -- Gitblit v1.6.2