linux/drivers/usb/renesas_usbhs/rcar3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Renesas USB driver R-Car Gen. 3 initialization and power control
   4 *
   5 * Copyright (C) 2016 Renesas Electronics Corporation
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/io.h>
  10#include "common.h"
  11#include "rcar3.h"
  12
  13#define LPSTS           0x102
  14#define UGCTRL          0x180   /* 32-bit register */
  15#define UGCTRL2         0x184   /* 32-bit register */
  16#define UGSTS           0x188   /* 32-bit register */
  17
  18/* Low Power Status register (LPSTS) */
  19#define LPSTS_SUSPM     0x4000
  20
  21/* R-Car D3 only: USB General control register (UGCTRL) */
  22#define UGCTRL_PLLRESET         0x00000001
  23#define UGCTRL_CONNECT          0x00000004
  24
  25/*
  26 * USB General control register 2 (UGCTRL2)
  27 * Remarks: bit[31:11] and bit[9:6] should be 0
  28 */
  29#define UGCTRL2_RESERVED_3      0x00000001      /* bit[3:0] should be B'0001 */
  30#define UGCTRL2_USB0SEL_EHCI    0x00000010
  31#define UGCTRL2_USB0SEL_HSUSB   0x00000020
  32#define UGCTRL2_USB0SEL_OTG     0x00000030
  33#define UGCTRL2_VBUSSEL         0x00000400
  34
  35/* R-Car D3 only: USB General status register (UGSTS) */
  36#define UGSTS_LOCK              0x00000100
  37
  38static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data)
  39{
  40        iowrite32(data, priv->base + reg);
  41}
  42
  43static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg)
  44{
  45        return ioread32(priv->base + reg);
  46}
  47
  48static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val)
  49{
  50        usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3);
  51}
  52
  53static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci)
  54{
  55        if (ehci)
  56                usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI);
  57        else
  58                usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB);
  59}
  60
  61static int usbhs_rcar3_power_ctrl(struct platform_device *pdev,
  62                                void __iomem *base, int enable)
  63{
  64        struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
  65
  66        usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL);
  67
  68        if (enable) {
  69                usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
  70                /* The controller on R-Car Gen3 needs to wait up to 45 usec */
  71                udelay(45);
  72        } else {
  73                usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0);
  74        }
  75
  76        return 0;
  77}
  78
  79/* R-Car D3 needs to release UGCTRL.PLLRESET */
  80static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev,
  81                                          void __iomem *base, int enable)
  82{
  83        struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
  84        u32 val;
  85        int timeout = 1000;
  86        bool is_host = false;
  87
  88        if (enable) {
  89                usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */
  90                if (priv->edev)
  91                        is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST);
  92
  93                usbhs_rcar3_set_usbsel(priv, is_host);
  94
  95                usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
  96                do {
  97                        val = usbhs_read32(priv, UGSTS);
  98                        udelay(1);
  99                } while (!(val & UGSTS_LOCK) && timeout--);
 100                usbhs_write32(priv, UGCTRL, UGCTRL_CONNECT);
 101        } else {
 102                usbhs_write32(priv, UGCTRL, 0);
 103                usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0);
 104                usbhs_write32(priv, UGCTRL, UGCTRL_PLLRESET);
 105        }
 106
 107        return 0;
 108}
 109
 110static int usbhs_rcar3_get_id(struct platform_device *pdev)
 111{
 112        return USBHS_GADGET;
 113}
 114
 115static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event,
 116                                void *data)
 117{
 118        struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb);
 119
 120        usbhs_rcar3_set_usbsel(priv, !!event);
 121
 122        return NOTIFY_DONE;
 123}
 124
 125const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
 126        .power_ctrl = usbhs_rcar3_power_ctrl,
 127        .get_id = usbhs_rcar3_get_id,
 128};
 129
 130const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = {
 131        .power_ctrl = usbhs_rcar3_power_and_pll_ctrl,
 132        .get_id = usbhs_rcar3_get_id,
 133        .notifier = usbhs_rcar3_notifier,
 134};
 135