linux/drivers/usb/mtu3/mtu3_dr.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 "mtu3.h"
  11#include "mtu3_dr.h"
  12#include "mtu3_debug.h"
  13
  14#define USB2_PORT 2
  15#define USB3_PORT 3
  16
  17static inline struct ssusb_mtk *otg_sx_to_ssusb(struct otg_switch_mtk *otg_sx)
  18{
  19        return container_of(otg_sx, struct ssusb_mtk, otg_switch);
  20}
  21
  22static void toggle_opstate(struct ssusb_mtk *ssusb)
  23{
  24        if (!ssusb->otg_switch.is_u3_drd) {
  25                mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION);
  26                mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN);
  27        }
  28}
  29
  30/* only port0 supports dual-role mode */
  31static int ssusb_port0_switch(struct ssusb_mtk *ssusb,
  32        int version, bool tohost)
  33{
  34        void __iomem *ibase = ssusb->ippc_base;
  35        u32 value;
  36
  37        dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__,
  38                version, tohost ? "host" : "device");
  39
  40        if (version == USB2_PORT) {
  41                /* 1. power off and disable u2 port0 */
  42                value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
  43                value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
  44                mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
  45
  46                /* 2. power on, enable u2 port0 and select its mode */
  47                value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
  48                value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
  49                value = tohost ? (value | SSUSB_U2_PORT_HOST_SEL) :
  50                        (value & (~SSUSB_U2_PORT_HOST_SEL));
  51                mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
  52        } else {
  53                /* 1. power off and disable u3 port0 */
  54                value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
  55                value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
  56                mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
  57
  58                /* 2. power on, enable u3 port0 and select its mode */
  59                value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
  60                value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
  61                value = tohost ? (value | SSUSB_U3_PORT_HOST_SEL) :
  62                        (value & (~SSUSB_U3_PORT_HOST_SEL));
  63                mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
  64        }
  65
  66        return 0;
  67}
  68
  69static void switch_port_to_host(struct ssusb_mtk *ssusb)
  70{
  71        u32 check_clk = 0;
  72
  73        dev_dbg(ssusb->dev, "%s\n", __func__);
  74
  75        ssusb_port0_switch(ssusb, USB2_PORT, true);
  76
  77        if (ssusb->otg_switch.is_u3_drd) {
  78                ssusb_port0_switch(ssusb, USB3_PORT, true);
  79                check_clk = SSUSB_U3_MAC_RST_B_STS;
  80        }
  81
  82        ssusb_check_clocks(ssusb, check_clk);
  83
  84        /* after all clocks are stable */
  85        toggle_opstate(ssusb);
  86}
  87
  88static void switch_port_to_device(struct ssusb_mtk *ssusb)
  89{
  90        u32 check_clk = 0;
  91
  92        dev_dbg(ssusb->dev, "%s\n", __func__);
  93
  94        ssusb_port0_switch(ssusb, USB2_PORT, false);
  95
  96        if (ssusb->otg_switch.is_u3_drd) {
  97                ssusb_port0_switch(ssusb, USB3_PORT, false);
  98                check_clk = SSUSB_U3_MAC_RST_B_STS;
  99        }
 100
 101        ssusb_check_clocks(ssusb, check_clk);
 102}
 103
 104int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
 105{
 106        struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
 107        struct regulator *vbus = otg_sx->vbus;
 108        int ret;
 109
 110        /* vbus is optional */
 111        if (!vbus)
 112                return 0;
 113
 114        dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
 115
 116        if (is_on) {
 117                ret = regulator_enable(vbus);
 118                if (ret) {
 119                        dev_err(ssusb->dev, "vbus regulator enable failed\n");
 120                        return ret;
 121                }
 122        } else {
 123                regulator_disable(vbus);
 124        }
 125
 126        return 0;
 127}
 128
 129static void ssusb_mode_sw_work(struct work_struct *work)
 130{
 131        struct otg_switch_mtk *otg_sx =
 132                container_of(work, struct otg_switch_mtk, dr_work);
 133        struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
 134        struct mtu3 *mtu = ssusb->u3d;
 135        enum usb_role desired_role = otg_sx->desired_role;
 136        enum usb_role current_role;
 137
 138        current_role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
 139
 140        if (desired_role == USB_ROLE_NONE) {
 141                /* the default mode is host as probe does */
 142                desired_role = USB_ROLE_HOST;
 143                if (otg_sx->default_role == USB_ROLE_DEVICE)
 144                        desired_role = USB_ROLE_DEVICE;
 145        }
 146
 147        if (current_role == desired_role)
 148                return;
 149
 150        dev_dbg(ssusb->dev, "set role : %s\n", usb_role_string(desired_role));
 151        mtu3_dbg_trace(ssusb->dev, "set role : %s", usb_role_string(desired_role));
 152        pm_runtime_get_sync(ssusb->dev);
 153
 154        switch (desired_role) {
 155        case USB_ROLE_HOST:
 156                ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
 157                mtu3_stop(mtu);
 158                switch_port_to_host(ssusb);
 159                ssusb_set_vbus(otg_sx, 1);
 160                ssusb->is_host = true;
 161                break;
 162        case USB_ROLE_DEVICE:
 163                ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
 164                ssusb->is_host = false;
 165                ssusb_set_vbus(otg_sx, 0);
 166                switch_port_to_device(ssusb);
 167                mtu3_start(mtu);
 168                break;
 169        case USB_ROLE_NONE:
 170        default:
 171                dev_err(ssusb->dev, "invalid role\n");
 172        }
 173        pm_runtime_put(ssusb->dev);
 174}
 175
 176static void ssusb_set_mode(struct otg_switch_mtk *otg_sx, enum usb_role role)
 177{
 178        struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
 179
 180        if (ssusb->dr_mode != USB_DR_MODE_OTG)
 181                return;
 182
 183        otg_sx->desired_role = role;
 184        queue_work(system_freezable_wq, &otg_sx->dr_work);
 185}
 186
 187static int ssusb_id_notifier(struct notifier_block *nb,
 188        unsigned long event, void *ptr)
 189{
 190        struct otg_switch_mtk *otg_sx =
 191                container_of(nb, struct otg_switch_mtk, id_nb);
 192
 193        ssusb_set_mode(otg_sx, event ? USB_ROLE_HOST : USB_ROLE_DEVICE);
 194
 195        return NOTIFY_DONE;
 196}
 197
 198static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
 199{
 200        struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
 201        struct extcon_dev *edev = otg_sx->edev;
 202        int ret;
 203
 204        /* extcon is optional */
 205        if (!edev)
 206                return 0;
 207
 208        otg_sx->id_nb.notifier_call = ssusb_id_notifier;
 209        ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST,
 210                                        &otg_sx->id_nb);
 211        if (ret < 0) {
 212                dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
 213                return ret;
 214        }
 215
 216        ret = extcon_get_state(edev, EXTCON_USB_HOST);
 217        dev_dbg(ssusb->dev, "EXTCON_USB_HOST: %d\n", ret);
 218
 219        /* default as host, switch to device mode if needed */
 220        if (!ret)
 221                ssusb_set_mode(otg_sx, USB_ROLE_DEVICE);
 222
 223        return 0;
 224}
 225
 226/*
 227 * We provide an interface via debugfs to switch between host and device modes
 228 * depending on user input.
 229 * This is useful in special cases, such as uses TYPE-A receptacle but also
 230 * wants to support dual-role mode.
 231 */
 232void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
 233{
 234        struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 235
 236        ssusb_set_mode(otg_sx, to_host ? USB_ROLE_HOST : USB_ROLE_DEVICE);
 237}
 238
 239void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
 240                          enum mtu3_dr_force_mode mode)
 241{
 242        u32 value;
 243
 244        value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
 245        switch (mode) {
 246        case MTU3_DR_FORCE_DEVICE:
 247                value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
 248                break;
 249        case MTU3_DR_FORCE_HOST:
 250                value |= SSUSB_U2_PORT_FORCE_IDDIG;
 251                value &= ~SSUSB_U2_PORT_RG_IDDIG;
 252                break;
 253        case MTU3_DR_FORCE_NONE:
 254                value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
 255                break;
 256        default:
 257                return;
 258        }
 259        mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
 260}
 261
 262static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
 263{
 264        struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
 265        struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 266
 267        ssusb_set_mode(otg_sx, role);
 268
 269        return 0;
 270}
 271
 272static enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw)
 273{
 274        struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
 275
 276        return ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
 277}
 278
 279static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
 280{
 281        struct usb_role_switch_desc role_sx_desc = { 0 };
 282        struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx);
 283        struct device *dev = ssusb->dev;
 284        enum usb_dr_mode mode;
 285
 286        if (!otg_sx->role_sw_used)
 287                return 0;
 288
 289        mode = usb_get_role_switch_default_mode(dev);
 290        if (mode == USB_DR_MODE_PERIPHERAL)
 291                otg_sx->default_role = USB_ROLE_DEVICE;
 292        else
 293                otg_sx->default_role = USB_ROLE_HOST;
 294
 295        role_sx_desc.set = ssusb_role_sw_set;
 296        role_sx_desc.get = ssusb_role_sw_get;
 297        role_sx_desc.fwnode = dev_fwnode(dev);
 298        role_sx_desc.driver_data = ssusb;
 299        otg_sx->role_sw = usb_role_switch_register(dev, &role_sx_desc);
 300        if (IS_ERR(otg_sx->role_sw))
 301                return PTR_ERR(otg_sx->role_sw);
 302
 303        ssusb_set_mode(otg_sx, otg_sx->default_role);
 304
 305        return 0;
 306}
 307
 308int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 309{
 310        struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 311        int ret = 0;
 312
 313        INIT_WORK(&otg_sx->dr_work, ssusb_mode_sw_work);
 314
 315        if (otg_sx->manual_drd_enabled)
 316                ssusb_dr_debugfs_init(ssusb);
 317        else if (otg_sx->role_sw_used)
 318                ret = ssusb_role_sw_register(otg_sx);
 319        else
 320                ret = ssusb_extcon_register(otg_sx);
 321
 322        return ret;
 323}
 324
 325void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
 326{
 327        struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 328
 329        cancel_work_sync(&otg_sx->dr_work);
 330        usb_role_switch_unregister(otg_sx->role_sw);
 331}
 332