summaryrefslogtreecommitdiff
path: root/drivers/usb/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/Kconfig12
-rw-r--r--drivers/usb/phy/Makefile7
-rw-r--r--drivers/usb/phy/rockchip_usb2_phy.c116
-rw-r--r--drivers/usb/phy/twl4030.c176
4 files changed, 311 insertions, 0 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
new file mode 100644
index 00000000000..c505862f1e1
--- /dev/null
+++ b/drivers/usb/phy/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2017
+# Adam Ford, Logic PD, aford173@gmail.com
+
+comment "USB Phy"
+
+config TWL4030_USB
+ bool "TWL4030 PHY"
+
+config ROCKCHIP_USB2_PHY
+ bool "Rockchip USB2 PHY"
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
new file mode 100644
index 00000000000..b67a70bbe8e
--- /dev/null
+++ b/drivers/usb/phy/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2009 Wind River Systems, Inc.
+# Tom Rix <Tom.Rix@windriver.com>
+
+obj-$(CONFIG_TWL4030_USB) += twl4030.o
+obj-$(CONFIG_ROCKCHIP_USB2_PHY) += rockchip_usb2_phy.o
diff --git a/drivers/usb/phy/rockchip_usb2_phy.c b/drivers/usb/phy/rockchip_usb2_phy.c
new file mode 100644
index 00000000000..ce9a7b5b819
--- /dev/null
+++ b/drivers/usb/phy/rockchip_usb2_phy.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2016 Rockchip Electronics Co., Ltd
+ */
+
+#include <hang.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include "../gadget/dwc2_udc_otg_priv.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BIT_WRITEABLE_SHIFT 16
+
+struct usb2phy_reg {
+ unsigned int offset;
+ unsigned int bitend;
+ unsigned int bitstart;
+ unsigned int disable;
+ unsigned int enable;
+};
+
+/**
+ * struct rockchip_usb2_phy_cfg: usb-phy port configuration
+ * @port_reset: usb otg per-port reset register
+ * @soft_con: software control usb otg register
+ * @suspend: phy suspend register
+ */
+struct rockchip_usb2_phy_cfg {
+ struct usb2phy_reg port_reset;
+ struct usb2phy_reg soft_con;
+ struct usb2phy_reg suspend;
+};
+
+struct rockchip_usb2_phy_dt_id {
+ char compatible[128];
+ const void *data;
+};
+
+static const struct rockchip_usb2_phy_cfg rk3066a_pdata = {
+ .port_reset = {0x00, 12, 12, 0, 1},
+ .soft_con = {0x08, 2, 2, 0, 1},
+ .suspend = {0x08, 8, 3, (0x01 << 3), (0x2A << 3)},
+};
+
+static const struct rockchip_usb2_phy_cfg rk3288_pdata = {
+ .port_reset = {0x00, 12, 12, 0, 1},
+ .soft_con = {0x08, 2, 2, 0, 1},
+ .suspend = {0x0c, 5, 0, 0x01, 0x2A},
+};
+
+static struct rockchip_usb2_phy_dt_id rockchip_usb2_phy_dt_ids[] = {
+ { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
+ { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3288_pdata },
+ { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
+ {}
+};
+
+static void property_enable(struct dwc2_plat_otg_data *pdata,
+ const struct usb2phy_reg *reg, bool en)
+{
+ unsigned int val, mask, tmp;
+
+ tmp = en ? reg->enable : reg->disable;
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+ writel(val, pdata->regs_phy + reg->offset);
+}
+
+void otg_phy_init(struct dwc2_udc *dev)
+{
+ struct dwc2_plat_otg_data *pdata = dev->pdata;
+ struct rockchip_usb2_phy_cfg *phy_cfg = NULL;
+ struct rockchip_usb2_phy_dt_id *of_id;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rockchip_usb2_phy_dt_ids); i++) {
+ of_id = &rockchip_usb2_phy_dt_ids[i];
+ if (ofnode_device_is_compatible(pdata->phy_of_node,
+ of_id->compatible)){
+ phy_cfg = (struct rockchip_usb2_phy_cfg *)of_id->data;
+ break;
+ }
+ }
+ if (!phy_cfg) {
+ debug("Can't find device platform data\n");
+
+ hang();
+ return;
+ }
+ pdata->priv = phy_cfg;
+ /* disable software control */
+ property_enable(pdata, &phy_cfg->soft_con, false);
+
+ /* reset otg port */
+ property_enable(pdata, &phy_cfg->port_reset, true);
+ mdelay(1);
+ property_enable(pdata, &phy_cfg->port_reset, false);
+ udelay(1);
+}
+
+void otg_phy_off(struct dwc2_udc *dev)
+{
+ struct dwc2_plat_otg_data *pdata = dev->pdata;
+ struct rockchip_usb2_phy_cfg *phy_cfg = pdata->priv;
+
+ /* enable software control */
+ property_enable(pdata, &phy_cfg->soft_con, true);
+ /* enter suspend */
+ property_enable(pdata, &phy_cfg->suspend, true);
+}
diff --git a/drivers/usb/phy/twl4030.c b/drivers/usb/phy/twl4030.c
new file mode 100644
index 00000000000..676868bead3
--- /dev/null
+++ b/drivers/usb/phy/twl4030.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2009 Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * This is file is based on
+ * repository git.gitorious.org/u-boot-omap3/mainline.git,
+ * branch omap3-dev-usb, file drivers/usb/gadget/twl4030_usb.c
+ *
+ * This is the unique part of its copyright :
+ *
+ * ------------------------------------------------------------------------
+ *
+ * * (C) Copyright 2009 Atin Malaviya (atin.malaviya@gmail.com)
+ *
+ * Based on: twl4030_usb.c in linux 2.6 (drivers/i2c/chips/twl4030_usb.c)
+ * Copyright (C) 2004-2007 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * Author: Atin Malaviya (atin.malaviya@gmail.com)
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#include <twl4030.h>
+#include <linux/delay.h>
+
+/* Defines for bits in registers */
+#define OPMODE_MASK (3 << 3)
+#define XCVRSELECT_MASK (3 << 0)
+#define CARKITMODE (1 << 2)
+#define OTG_ENAB (1 << 5)
+#define PHYPWD (1 << 0)
+#define CLOCKGATING_EN (1 << 2)
+#define CLK32K_EN (1 << 1)
+#define REQ_PHY_DPLL_CLK (1 << 0)
+#define PHY_DPLL_CLK (1 << 0)
+
+static int twl4030_usb_write(u8 address, u8 data)
+{
+ int ret;
+
+ ret = twl4030_i2c_write_u8(TWL4030_CHIP_USB, address, data);
+ if (ret != 0)
+ printf("TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
+
+ return ret;
+}
+
+static int twl4030_usb_read(u8 address)
+{
+ u8 data;
+ int ret;
+
+ ret = twl4030_i2c_read_u8(TWL4030_CHIP_USB, address, &data);
+ if (ret == 0)
+ ret = data;
+ else
+ printf("TWL4030:USB:Read[0x%x] Error %d\n", address, ret);
+
+ return ret;
+}
+
+static void twl4030_usb_ldo_init(void)
+{
+ /* Enable writing to power configuration registers */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER,
+ TWL4030_PM_MASTER_PROTECT_KEY, 0xC0);
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER,
+ TWL4030_PM_MASTER_PROTECT_KEY, 0x0C);
+
+ /* put VUSB3V1 LDO in active state */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB_DEDICATED2, 0x00);
+
+ /* input to VUSB3V1 LDO is from VBAT, not VBUS */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB_DEDICATED1, 0x14);
+
+ /* turn on 3.1V regulator */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB3V1_DEV_GRP, 0x20);
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB3V1_TYPE, 0x00);
+
+ /* turn on 1.5V regulator */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB1V5_DEV_GRP, 0x20);
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB1V5_TYPE, 0x00);
+
+ /* turn on 1.8V regulator */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB1V8_DEV_GRP, 0x20);
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER,
+ TWL4030_PM_RECEIVER_VUSB1V8_TYPE, 0x00);
+
+ /* disable access to power configuration registers */
+ twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER,
+ TWL4030_PM_MASTER_PROTECT_KEY, 0x00);
+}
+
+static void twl4030_phy_power(void)
+{
+ u8 pwr, clk;
+
+ /* Power the PHY */
+ pwr = twl4030_usb_read(TWL4030_USB_PHY_PWR_CTRL);
+ pwr &= ~PHYPWD;
+ twl4030_usb_write(TWL4030_USB_PHY_PWR_CTRL, pwr);
+ /* Enable clocks */
+ clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL);
+ clk |= CLOCKGATING_EN | CLK32K_EN;
+ twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk);
+}
+
+/*
+ * Initiaze the ULPI interface
+ * ULPI : Universal Transceiver Macrocell Low Pin Interface
+ * An interface between the USB link controller like musb and the
+ * the PHY or transceiver that drives the actual bus.
+ */
+int twl4030_usb_ulpi_init(void)
+{
+ long timeout = 1000 * 1000; /* 1 sec */;
+ u8 clk, sts, pwr;
+
+ /* twl4030 ldo init */
+ twl4030_usb_ldo_init();
+
+ /* Enable the twl4030 phy */
+ twl4030_phy_power();
+
+ /* Enable DPLL to access PHY registers over I2C */
+ clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL);
+ clk |= REQ_PHY_DPLL_CLK;
+ twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk);
+
+ /* Check if the PHY DPLL is locked */
+ sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
+ while (!(sts & PHY_DPLL_CLK) && 0 < timeout) {
+ udelay(10);
+ sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
+ timeout -= 10;
+ }
+
+ /* Final check */
+ sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
+ if (!(sts & PHY_DPLL_CLK)) {
+ printf("Error:TWL4030:USB Timeout setting PHY DPLL clock\n");
+ return -1;
+ }
+
+ /*
+ * There are two circuit blocks attached to the PHY,
+ * Carkit and USB OTG. Disable Carkit and enable USB OTG
+ */
+ twl4030_usb_write(TWL4030_USB_IFC_CTRL_CLR, CARKITMODE);
+ pwr = twl4030_usb_read(TWL4030_USB_POWER_CTRL);
+ pwr |= OTG_ENAB;
+ twl4030_usb_write(TWL4030_USB_POWER_CTRL_SET, pwr);
+
+ /* Clear the opmode bits to ensure normal encode */
+ twl4030_usb_write(TWL4030_USB_FUNC_CTRL_CLR, OPMODE_MASK);
+
+ /* Clear the xcvrselect bits to enable the high speed transeiver */
+ twl4030_usb_write(TWL4030_USB_FUNC_CTRL_CLR, XCVRSELECT_MASK);
+
+ /* Let ULPI control the DPLL clock */
+ clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL);
+ clk &= ~REQ_PHY_DPLL_CLK;
+ twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk);
+
+ return 0;
+}