usb: mtu3: supports remote wakeup for mt2712 with two SSUSB IPs
The old way of usb wakeup only supports platform with single SSUSB IP, such as mt8173, but mt2712 has two SSUSB IPs, so rebuild its flow and also supports the new glue layer of usb wakeup on mt2712 which is different from mt8173. Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
3849c29013
commit
f0ede2c628
|
@ -229,7 +229,10 @@ struct otg_switch_mtk {
|
|||
* @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to
|
||||
* disable u3port0, bit1==1 to disable u3port1,... etc
|
||||
* @dbgfs_root: only used when supports manual dual-role switch via debugfs
|
||||
* @wakeup_en: it's true when supports remote wakeup in host mode
|
||||
* @uwk_en: it's true when supports remote wakeup in host mode
|
||||
* @uwk: syscon including usb wakeup glue layer between SSUSB IP and SPM
|
||||
* @uwk_reg_base: the base address of the wakeup glue layer in @uwk
|
||||
* @uwk_vers: the version of the wakeup glue layer
|
||||
*/
|
||||
struct ssusb_mtk {
|
||||
struct device *dev;
|
||||
|
@ -253,8 +256,10 @@ struct ssusb_mtk {
|
|||
int u3p_dis_msk;
|
||||
struct dentry *dbgfs_root;
|
||||
/* usb wakeup for host mode */
|
||||
bool wakeup_en;
|
||||
struct regmap *pericfg;
|
||||
bool uwk_en;
|
||||
struct regmap *uwk;
|
||||
u32 uwk_reg_base;
|
||||
u32 uwk_vers;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,8 +18,7 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
|
|||
struct device_node *dn);
|
||||
int ssusb_host_enable(struct ssusb_mtk *ssusb);
|
||||
int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
|
||||
int ssusb_wakeup_enable(struct ssusb_mtk *ssusb);
|
||||
void ssusb_wakeup_disable(struct ssusb_mtk *ssusb);
|
||||
void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable);
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -18,66 +18,77 @@
|
|||
#include "mtu3.h"
|
||||
#include "mtu3_dr.h"
|
||||
|
||||
#define PERI_WK_CTRL1 0x404
|
||||
#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
|
||||
#define UWK_CTL1_IS_E BIT(25)
|
||||
#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
|
||||
#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
|
||||
#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
|
||||
#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
|
||||
/* mt8173 etc */
|
||||
#define PERI_WK_CTRL1 0x4
|
||||
#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
|
||||
#define WC1_IS_EN BIT(25)
|
||||
#define WC1_IS_P BIT(6) /* polarity for ip sleep */
|
||||
|
||||
/* mt2712 etc */
|
||||
#define PERI_SSUSB_SPM_CTRL 0x0
|
||||
#define SSC_IP_SLEEP_EN BIT(4)
|
||||
#define SSC_SPM_INT_EN BIT(1)
|
||||
|
||||
enum ssusb_uwk_vers {
|
||||
SSUSB_UWK_V1 = 1,
|
||||
SSUSB_UWK_V2,
|
||||
};
|
||||
|
||||
/*
|
||||
* ip-sleep wakeup mode:
|
||||
* all clocks can be turn off, but power domain should be kept on
|
||||
*/
|
||||
static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb)
|
||||
static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable)
|
||||
{
|
||||
u32 tmp;
|
||||
struct regmap *pericfg = ssusb->pericfg;
|
||||
u32 reg, msk, val;
|
||||
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_IS_P;
|
||||
tmp &= ~(UWK_CTL1_IS_C(0xf));
|
||||
tmp |= UWK_CTL1_IS_C(0x8);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp);
|
||||
regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
|
||||
|
||||
regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
|
||||
dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
|
||||
__func__, tmp);
|
||||
}
|
||||
|
||||
static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp);
|
||||
tmp &= ~UWK_CTL1_IS_E;
|
||||
regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp);
|
||||
switch (ssusb->uwk_vers) {
|
||||
case SSUSB_UWK_V1:
|
||||
reg = ssusb->uwk_reg_base + PERI_WK_CTRL1;
|
||||
msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
|
||||
val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
|
||||
break;
|
||||
case SSUSB_UWK_V2:
|
||||
reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
|
||||
msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
|
||||
val = enable ? msk : 0;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
regmap_update_bits(ssusb->uwk, reg, msk, val);
|
||||
}
|
||||
|
||||
int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct device *dev = ssusb->dev;
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Wakeup function is optional, so it is not an error if this property
|
||||
* does not exist, and in such case, no need to get relative
|
||||
* properties anymore.
|
||||
*/
|
||||
ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup");
|
||||
if (!ssusb->wakeup_en)
|
||||
/* wakeup function is optional */
|
||||
ssusb->uwk_en = of_property_read_bool(dn, "wakeup-source");
|
||||
if (!ssusb->uwk_en)
|
||||
return 0;
|
||||
|
||||
ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,
|
||||
"mediatek,syscon-wakeup");
|
||||
if (IS_ERR(ssusb->pericfg)) {
|
||||
dev_err(dev, "fail to get pericfg regs\n");
|
||||
return PTR_ERR(ssusb->pericfg);
|
||||
}
|
||||
ret = of_parse_phandle_with_fixed_args(dn,
|
||||
"mediatek,syscon-wakeup", 2, 0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
ssusb->uwk_reg_base = args.args[0];
|
||||
ssusb->uwk_vers = args.args[1];
|
||||
ssusb->uwk = syscon_node_to_regmap(args.np);
|
||||
of_node_put(args.np);
|
||||
dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n",
|
||||
ssusb->uwk_reg_base, ssusb->uwk_vers);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ssusb->uwk);
|
||||
}
|
||||
|
||||
void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable)
|
||||
{
|
||||
if (ssusb->uwk_en)
|
||||
ssusb_wakeup_ip_sleep_set(ssusb, enable);
|
||||
}
|
||||
|
||||
static void host_ports_num_get(struct ssusb_mtk *ssusb)
|
||||
|
@ -235,17 +246,3 @@ void ssusb_host_exit(struct ssusb_mtk *ssusb)
|
|||
of_platform_depopulate(ssusb->dev);
|
||||
ssusb_host_cleanup(ssusb);
|
||||
}
|
||||
|
||||
int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
if (ssusb->wakeup_en)
|
||||
ssusb_wakeup_ip_sleep_en(ssusb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
if (ssusb->wakeup_en)
|
||||
ssusb_wakeup_ip_sleep_dis(ssusb);
|
||||
}
|
||||
|
|
|
@ -282,8 +282,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
|
|||
|
||||
/* if host role is supported */
|
||||
ret = ssusb_wakeup_of_property_parse(ssusb, node);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to parse uwk property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* optional property, ignore the error if it does not exist */
|
||||
of_property_read_u32(node, "mediatek,u3p-dis-msk",
|
||||
|
@ -457,7 +459,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)
|
|||
ssusb_host_disable(ssusb, true);
|
||||
ssusb_phy_power_off(ssusb);
|
||||
ssusb_clks_disable(ssusb);
|
||||
ssusb_wakeup_enable(ssusb);
|
||||
ssusb_wakeup_set(ssusb, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -473,7 +475,7 @@ static int __maybe_unused mtu3_resume(struct device *dev)
|
|||
if (!ssusb->is_host)
|
||||
return 0;
|
||||
|
||||
ssusb_wakeup_disable(ssusb);
|
||||
ssusb_wakeup_set(ssusb, false);
|
||||
ret = ssusb_clks_enable(ssusb);
|
||||
if (ret)
|
||||
goto clks_err;
|
||||
|
|
Loading…
Reference in New Issue