linux/drivers/usb/mtu3/mtu3_host.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * mtu3_dr.c - dual role switch and host glue layer
   4 *
   5 * Copyright (C) 2016 MediaTek Inc.
   6 *
   7 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/irq.h>
  12#include <linux/kernel.h>
  13#include <linux/mfd/syscon.h>
  14#include <linux/of_device.h>
  15#include <linux/regmap.h>
  16
  17#include "mtu3.h"
  18#include "mtu3_dr.h"
  19
  20/* mt8173 etc */
  21#define PERI_WK_CTRL1   0x4
  22#define WC1_IS_C(x)     (((x) & 0xf) << 26)  /* cycle debounce */
  23#define WC1_IS_EN       BIT(25)
  24#define WC1_IS_P        BIT(6)  /* polarity for ip sleep */
  25
  26/* mt8183 */
  27#define PERI_WK_CTRL0   0x0
  28#define WC0_IS_C(x)     ((u32)(((x) & 0xf) << 28))  /* cycle debounce */
  29#define WC0_IS_P        BIT(12) /* polarity */
  30#define WC0_IS_EN       BIT(6)
  31
  32/* mt8192 */
  33#define WC0_SSUSB0_CDEN         BIT(6)
  34#define WC0_IS_SPM_EN           BIT(1)
  35
  36/* mt2712 etc */
  37#define PERI_SSUSB_SPM_CTRL     0x0
  38#define SSC_IP_SLEEP_EN BIT(4)
  39#define SSC_SPM_INT_EN          BIT(1)
  40
  41enum ssusb_uwk_vers {
  42        SSUSB_UWK_V1 = 1,
  43        SSUSB_UWK_V2,
  44        SSUSB_UWK_V1_1 = 101,   /* specific revision 1.01 */
  45        SSUSB_UWK_V1_2,         /* specific revision 1.02 */
  46};
  47
  48/*
  49 * ip-sleep wakeup mode:
  50 * all clocks can be turn off, but power domain should be kept on
  51 */
  52static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable)
  53{
  54        u32 reg, msk, val;
  55
  56        switch (ssusb->uwk_vers) {
  57        case SSUSB_UWK_V1:
  58                reg = ssusb->uwk_reg_base + PERI_WK_CTRL1;
  59                msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
  60                val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
  61                break;
  62        case SSUSB_UWK_V1_1:
  63                reg = ssusb->uwk_reg_base + PERI_WK_CTRL0;
  64                msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P;
  65                val = enable ? (WC0_IS_EN | WC0_IS_C(0x1)) : 0;
  66                break;
  67        case SSUSB_UWK_V1_2:
  68                reg = ssusb->uwk_reg_base + PERI_WK_CTRL0;
  69                msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN;
  70                val = enable ? msk : 0;
  71                break;
  72        case SSUSB_UWK_V2:
  73                reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
  74                msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
  75                val = enable ? msk : 0;
  76                break;
  77        default:
  78                return;
  79        }
  80        regmap_update_bits(ssusb->uwk, reg, msk, val);
  81}
  82
  83int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
  84                                struct device_node *dn)
  85{
  86        struct of_phandle_args args;
  87        int ret;
  88
  89        /* wakeup function is optional */
  90        ssusb->uwk_en = of_property_read_bool(dn, "wakeup-source");
  91        if (!ssusb->uwk_en)
  92                return 0;
  93
  94        ret = of_parse_phandle_with_fixed_args(dn,
  95                                "mediatek,syscon-wakeup", 2, 0, &args);
  96        if (ret)
  97                return ret;
  98
  99        ssusb->uwk_reg_base = args.args[0];
 100        ssusb->uwk_vers = args.args[1];
 101        ssusb->uwk = syscon_node_to_regmap(args.np);
 102        of_node_put(args.np);
 103        dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n",
 104                        ssusb->uwk_reg_base, ssusb->uwk_vers);
 105
 106        return PTR_ERR_OR_ZERO(ssusb->uwk);
 107}
 108
 109void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable)
 110{
 111        if (ssusb->uwk_en)
 112                ssusb_wakeup_ip_sleep_set(ssusb, enable);
 113}
 114
 115static void host_ports_num_get(struct ssusb_mtk *ssusb)
 116{
 117        u32 xhci_cap;
 118
 119        xhci_cap = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
 120        ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
 121        ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
 122
 123        dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n",
 124                 ssusb->u2_ports, ssusb->u3_ports);
 125}
 126
 127/* only configure ports will be used later */
 128static int ssusb_host_enable(struct ssusb_mtk *ssusb)
 129{
 130        void __iomem *ibase = ssusb->ippc_base;
 131        int num_u3p = ssusb->u3_ports;
 132        int num_u2p = ssusb->u2_ports;
 133        int u3_ports_disabled;
 134        u32 check_clk;
 135        u32 value;
 136        int i;
 137
 138        /* power on host ip */
 139        mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
 140
 141        /* power on and enable u3 ports except skipped ones */
 142        u3_ports_disabled = 0;
 143        for (i = 0; i < num_u3p; i++) {
 144                if ((0x1 << i) & ssusb->u3p_dis_msk) {
 145                        u3_ports_disabled++;
 146                        continue;
 147                }
 148
 149                value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
 150                value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
 151                value |= SSUSB_U3_PORT_HOST_SEL;
 152                mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
 153        }
 154
 155        /* power on and enable all u2 ports */
 156        for (i = 0; i < num_u2p; i++) {
 157                if ((0x1 << i) & ssusb->u2p_dis_msk)
 158                        continue;
 159
 160                value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
 161                value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
 162                value |= SSUSB_U2_PORT_HOST_SEL;
 163                mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
 164        }
 165
 166        check_clk = SSUSB_XHCI_RST_B_STS;
 167        if (num_u3p > u3_ports_disabled)
 168                check_clk = SSUSB_U3_MAC_RST_B_STS;
 169
 170        return ssusb_check_clocks(ssusb, check_clk);
 171}
 172
 173static int ssusb_host_disable(struct ssusb_mtk *ssusb)
 174{
 175        void __iomem *ibase = ssusb->ippc_base;
 176        int num_u3p = ssusb->u3_ports;
 177        int num_u2p = ssusb->u2_ports;
 178        u32 value;
 179        int i;
 180
 181        /* power down and disable u3 ports except skipped ones */
 182        for (i = 0; i < num_u3p; i++) {
 183                if ((0x1 << i) & ssusb->u3p_dis_msk)
 184                        continue;
 185
 186                value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
 187                value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
 188                mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
 189        }
 190
 191        /* power down and disable u2 ports except skipped ones */
 192        for (i = 0; i < num_u2p; i++) {
 193                if ((0x1 << i) & ssusb->u2p_dis_msk)
 194                        continue;
 195
 196                value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
 197                value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
 198                mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
 199        }
 200
 201        /* power down host ip */
 202        mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
 203
 204        return 0;
 205}
 206
 207int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped)
 208{
 209        void __iomem *ibase = ssusb->ippc_base;
 210        int u3p_skip_msk = ssusb->u3p_dis_msk;
 211        int u2p_skip_msk = ssusb->u2p_dis_msk;
 212        int num_u3p = ssusb->u3_ports;
 213        int num_u2p = ssusb->u2_ports;
 214        u32 value;
 215        int i;
 216
 217        if (p0_skipped) {
 218                u2p_skip_msk |= 0x1;
 219                if (ssusb->otg_switch.is_u3_drd)
 220                        u3p_skip_msk |= 0x1;
 221        }
 222
 223        /* power on host ip */
 224        mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
 225
 226        /* power on u3 ports except skipped ones */
 227        for (i = 0; i < num_u3p; i++) {
 228                if ((0x1 << i) & u3p_skip_msk)
 229                        continue;
 230
 231                value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
 232                value &= ~SSUSB_U3_PORT_PDN;
 233                mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
 234        }
 235
 236        /* power on all u2 ports except skipped ones */
 237        for (i = 0; i < num_u2p; i++) {
 238                if ((0x1 << i) & u2p_skip_msk)
 239                        continue;
 240
 241                value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
 242                value &= ~SSUSB_U2_PORT_PDN;
 243                mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
 244        }
 245
 246        return 0;
 247}
 248
 249/* here not skip port0 due to PDN can be set repeatedly */
 250int ssusb_host_suspend(struct ssusb_mtk *ssusb)
 251{
 252        void __iomem *ibase = ssusb->ippc_base;
 253        int num_u3p = ssusb->u3_ports;
 254        int num_u2p = ssusb->u2_ports;
 255        u32 value;
 256        int i;
 257
 258        /* power down u3 ports except skipped ones */
 259        for (i = 0; i < num_u3p; i++) {
 260                if ((0x1 << i) & ssusb->u3p_dis_msk)
 261                        continue;
 262
 263                value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
 264                value |= SSUSB_U3_PORT_PDN;
 265                mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
 266        }
 267
 268        /* power down u2 ports except skipped ones */
 269        for (i = 0; i < num_u2p; i++) {
 270                if ((0x1 << i) & ssusb->u2p_dis_msk)
 271                        continue;
 272
 273                value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
 274                value |= SSUSB_U2_PORT_PDN;
 275                mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
 276        }
 277
 278        /* power down host ip */
 279        mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
 280
 281        return 0;
 282}
 283
 284static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 285{
 286        host_ports_num_get(ssusb);
 287
 288        /*
 289         * power on host and power on/enable all ports
 290         * if support OTG, gadget driver will switch port0 to device mode
 291         */
 292        ssusb_host_enable(ssusb);
 293        ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
 294
 295        /* if port0 supports dual-role, works as host mode by default */
 296        ssusb_set_vbus(&ssusb->otg_switch, 1);
 297}
 298
 299static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
 300{
 301        if (ssusb->is_host)
 302                ssusb_set_vbus(&ssusb->otg_switch, 0);
 303
 304        ssusb_host_disable(ssusb);
 305}
 306
 307/*
 308 * If host supports multiple ports, the VBUSes(5V) of ports except port0
 309 * which supports OTG are better to be enabled by default in DTS.
 310 * Because the host driver will keep link with devices attached when system
 311 * enters suspend mode, so no need to control VBUSes after initialization.
 312 */
 313int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn)
 314{
 315        struct device *parent_dev = ssusb->dev;
 316        int ret;
 317
 318        ssusb_host_setup(ssusb);
 319
 320        ret = of_platform_populate(parent_dn, NULL, NULL, parent_dev);
 321        if (ret) {
 322                dev_dbg(parent_dev, "failed to create child devices at %pOF\n",
 323                                parent_dn);
 324                return ret;
 325        }
 326
 327        dev_info(parent_dev, "xHCI platform device register success...\n");
 328
 329        return 0;
 330}
 331
 332void ssusb_host_exit(struct ssusb_mtk *ssusb)
 333{
 334        of_platform_depopulate(ssusb->dev);
 335        ssusb_host_cleanup(ssusb);
 336}
 337