[U-Boot] [PATCH 10/17] musb-new: Add sunxi musb controller support

Hans de Goede hdegoede at redhat.com
Sun Jan 11 20:34:48 CET 2015


This is based on Jussi Kivilinna's work for the linux-sunxi-3.4 kernel to use
the kernels musb driver instead of Allwinners own custom driver.

Signed-off-by: Hans de Goede <hdegoede at redhat.com>
---
 drivers/usb/musb-new/Makefile |   1 +
 drivers/usb/musb-new/sunxi.c  | 279 ++++++++++++++++++++++++++++++++++++++++++
 include/usb.h                 |   4 +-
 3 files changed, 282 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/musb-new/sunxi.c

diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile
index 3facf0f..9edeece 100644
--- a/drivers/usb/musb-new/Makefile
+++ b/drivers/usb/musb-new/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o
 obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o
 obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o
 obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o
+obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
 
 ccflags-y := $(call cc-option,-Wno-unused-variable) \
 		$(call cc-option,-Wno-unused-but-set-variable) \
diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c
new file mode 100644
index 0000000..778916d
--- /dev/null
+++ b/drivers/usb/musb-new/sunxi.c
@@ -0,0 +1,279 @@
+/*
+ * Allwinner SUNXI "glue layer"
+ *
+ * Copyright © 2015 Hans de Goede <hdegoede at redhat.com>
+ * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * Based on the sw_usb "Allwinner OTG Dual Role Controller" code.
+ *  Copyright 2007-2012 (C) Allwinner Technology Co., Ltd.
+ *  javen <javen at allwinnertech.com>
+ *
+ * Based on the DA8xx "glue layer" code.
+ *  Copyright (c) 2008-2009 MontaVista Software, Inc. <source at mvista.com>
+ *  Copyright (C) 2005-2006 by Texas Instruments
+ *
+ * This file is part of the Inventra Controller Driver for Linux.
+ *
+ * The Inventra Controller Driver for Linux is free software; you
+ * can redistribute it and/or modify it under the terms of the GNU
+ * General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ */
+#include <common.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/usbc.h>
+#include "linux-compat.h"
+#include "musb_core.h"
+
+/******************************************************************************
+ ******************************************************************************
+ * From the Allwinner driver
+ ******************************************************************************
+ ******************************************************************************/
+
+/******************************************************************************
+ * From include/sunxi_usb_bsp.h
+ ******************************************************************************/
+
+/* reg offsets */
+#define  USBC_REG_o_ISCR	0x0400
+#define  USBC_REG_o_PHYCTL	0x0404
+#define  USBC_REG_o_PHYBIST	0x0408
+#define  USBC_REG_o_PHYTUNE	0x040c
+
+#define  USBC_REG_o_VEND0	0x0043
+
+/* Interface Status and Control */
+#define  USBC_BP_ISCR_VBUS_VALID_FROM_DATA	30
+#define  USBC_BP_ISCR_VBUS_VALID_FROM_VBUS	29
+#define  USBC_BP_ISCR_EXT_ID_STATUS		28
+#define  USBC_BP_ISCR_EXT_DM_STATUS		27
+#define  USBC_BP_ISCR_EXT_DP_STATUS		26
+#define  USBC_BP_ISCR_MERGED_VBUS_STATUS	25
+#define  USBC_BP_ISCR_MERGED_ID_STATUS		24
+
+#define  USBC_BP_ISCR_ID_PULLUP_EN		17
+#define  USBC_BP_ISCR_DPDM_PULLUP_EN		16
+#define  USBC_BP_ISCR_FORCE_ID			14
+#define  USBC_BP_ISCR_FORCE_VBUS_VALID		12
+#define  USBC_BP_ISCR_VBUS_VALID_SRC		10
+
+#define  USBC_BP_ISCR_HOSC_EN			7
+#define  USBC_BP_ISCR_VBUS_CHANGE_DETECT	6
+#define  USBC_BP_ISCR_ID_CHANGE_DETECT		5
+#define  USBC_BP_ISCR_DPDM_CHANGE_DETECT	4
+#define  USBC_BP_ISCR_IRQ_ENABLE		3
+#define  USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN	2
+#define  USBC_BP_ISCR_ID_CHANGE_DETECT_EN	1
+#define  USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN	0
+
+/******************************************************************************
+ * From usbc/usbc.c
+ ******************************************************************************/
+
+static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val)
+{
+	u32 temp = reg_val;
+
+	temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT);
+	temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT);
+	temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT);
+
+	return temp;
+}
+
+static void USBC_EnableIdPullUp(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_DisableIdPullUp(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_EnableDpDmPullUp(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_DisableDpDmPullUp(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_ForceIdToLow(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID);
+	reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_ForceIdToHigh(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID);
+	reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_ForceVbusValidDisable(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_ForceVbusValidToHigh(__iomem void *base)
+{
+	u32 reg_val;
+
+	reg_val = musb_readl(base, USBC_REG_o_ISCR);
+	reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
+	reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
+	reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
+	musb_writel(base, USBC_REG_o_ISCR, reg_val);
+}
+
+static void USBC_ConfigFIFO_Base(void)
+{
+	u32 reg_value;
+
+	/* config usb fifo, 8kb mode */
+	reg_value = readl(SUNXI_SRAMC_BASE + 0x04);
+	reg_value &= ~(0x03 << 0);
+	reg_value |= (1 << 0);
+	writel(reg_value, SUNXI_SRAMC_BASE + 0x04);
+}
+
+/******************************************************************************
+ * MUSB Glue code
+ ******************************************************************************/
+
+static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
+{
+	struct musb		*musb = __hci;
+	irqreturn_t		retval = IRQ_NONE;
+
+	/* read and flush interrupts */
+	musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+	if (musb->int_usb)
+		musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
+	musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+	if (musb->int_tx)
+		musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
+	musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+	if (musb->int_rx)
+		musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
+
+	if (musb->int_usb || musb->int_tx || musb->int_rx)
+		retval |= musb_interrupt(musb);
+
+	return retval;
+}
+
+static void sunxi_musb_enable(struct musb *musb)
+{
+	pr_debug("%s():\n", __func__);
+
+	/* select PIO mode */
+	musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0);
+
+	if (is_host_enabled(musb)) {
+		/* port power on */
+		sunxi_usbc_vbus_enable(0);
+	}
+}
+
+static void sunxi_musb_disable(struct musb *musb)
+{
+	pr_debug("%s():\n", __func__);
+
+	/* Put the controller back in a pristane state for "usb reset" */
+	if (musb->is_active) {
+		sunxi_usbc_disable(0);
+		sunxi_usbc_enable(0);
+		musb->is_active = 0;
+	}
+}
+
+static int sunxi_musb_init(struct musb *musb)
+{
+	int err;
+
+	pr_debug("%s():\n", __func__);
+
+	err = sunxi_usbc_request_resources(0);
+	if (err)
+		return err;
+
+	musb->isr = sunxi_musb_interrupt;
+	sunxi_usbc_enable(0);
+
+	USBC_ConfigFIFO_Base();
+	USBC_EnableDpDmPullUp(musb->mregs);
+	USBC_EnableIdPullUp(musb->mregs);
+
+	if (is_host_enabled(musb)) {
+		/* Host mode */
+		USBC_ForceIdToLow(musb->mregs);
+		USBC_ForceVbusValidToHigh(musb->mregs);
+	} else {
+		/* Peripheral mode */
+		USBC_ForceIdToHigh(musb->mregs);
+		USBC_ForceVbusValidDisable(musb->mregs);
+	}
+
+	return 0;
+}
+
+static int sunxi_musb_exit(struct musb *musb)
+{
+	pr_debug("%s():\n", __func__);
+
+	USBC_DisableDpDmPullUp(musb->mregs);
+	USBC_DisableIdPullUp(musb->mregs);
+	sunxi_usbc_vbus_disable(0);
+	sunxi_usbc_disable(0);
+
+	return sunxi_usbc_free_resources(0);
+}
+
+const struct musb_platform_ops sunxi_musb_ops = {
+	.init		= sunxi_musb_init,
+	.exit		= sunxi_musb_exit,
+
+	.enable		= sunxi_musb_enable,
+	.disable	= sunxi_musb_disable,
+};
diff --git a/include/usb.h b/include/usb.h
index 3d33b2d..b921a7f 100644
--- a/include/usb.h
+++ b/include/usb.h
@@ -154,8 +154,8 @@ enum usb_init_type {
 	defined(CONFIG_USB_OMAP3) || defined(CONFIG_USB_DA8XX) || \
 	defined(CONFIG_USB_BLACKFIN) || defined(CONFIG_USB_AM35X) || \
 	defined(CONFIG_USB_MUSB_DSPS) || defined(CONFIG_USB_MUSB_AM35X) || \
-	defined(CONFIG_USB_MUSB_OMAP2PLUS) || defined(CONFIG_USB_XHCI) || \
-	defined(CONFIG_USB_DWC2)
+	defined(CONFIG_USB_MUSB_OMAP2PLUS) || defined(CONFIG_USB_MUSB_SUNXI) || \
+	defined(CONFIG_USB_XHCI) || defined(CONFIG_USB_DWC2)
 
 int usb_lowlevel_init(int index, enum usb_init_type init, void **controller);
 int usb_lowlevel_stop(int index);
-- 
2.1.0



More information about the U-Boot mailing list