usb: Add MediaTek USB3 DRD driver

This patch adds support for the MediaTek USB3 controller
integrated into MT8173. It currently supports High-Speed
Peripheral Only mode.

Super-Speed Peripheral, Dual-Role Device and Host Only (xHCI)
modes will be added in the next patchs.

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Chunfeng Yun
2016-10-19 10:28:23 +08:00
committed by Greg Kroah-Hartman
parent 065d48cf40
commit df2069acb0
12 changed files with 3860 additions and 0 deletions
+2
View File
@@ -95,6 +95,8 @@ source "drivers/usb/usbip/Kconfig"
endif
source "drivers/usb/mtu3/Kconfig"
source "drivers/usb/musb/Kconfig"
source "drivers/usb/dwc3/Kconfig"
+1
View File
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/
obj-$(CONFIG_PCI) += host/
obj-$(CONFIG_USB_EHCI_HCD) += host/
+32
View File
@@ -0,0 +1,32 @@
# For MTK USB3.0 IP
config USB_MTU3
tristate "MediaTek USB3 Dual Role controller"
depends on (USB || USB_GADGET) && HAS_DMA
depends on ARCH_MEDIATEK || COMPILE_TEST
help
Say Y or M here if your system runs on MediaTek SoCs with
Dual Role SuperSpeed USB controller. You can select usb
mode as peripheral role or host role, or both.
If you don't know what this is, please say N.
Choose M here to compile this driver as a module, and it
will be called mtu3.ko.
if USB_MTU3
choice
bool "MTU3 Mode Selection"
default USB_MTU3_GADGET if (!USB && USB_GADGET)
config USB_MTU3_GADGET
bool "Gadget only mode"
depends on USB_GADGET=y || USB_GADGET=USB_MTU3
help
Select this when you want to use MTU3 in gadget mode only,
thereby the host feature will be regressed.
endchoice
endif
+2
View File
@@ -0,0 +1,2 @@
obj-$(CONFIG_USB_MTU3) += mtu3.o
mtu3-y := mtu3_plat.o mtu3_core.o mtu3_gadget_ep0.o mtu3_gadget.o mtu3_qmu.o
+341
View File
@@ -0,0 +1,341 @@
/*
* mtu3.h - MediaTek USB3 DRD header
*
* Copyright (C) 2016 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __MTU3_H__
#define __MTU3_H__
#include <linux/device.h>
#include <linux/dmapool.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
struct mtu3;
struct mtu3_ep;
struct mtu3_request;
#include "mtu3_hw_regs.h"
#include "mtu3_qmu.h"
#define MU3D_EP_TXCR0(epnum) (U3D_TX1CSR0 + (((epnum) - 1) * 0x10))
#define MU3D_EP_TXCR1(epnum) (U3D_TX1CSR1 + (((epnum) - 1) * 0x10))
#define MU3D_EP_TXCR2(epnum) (U3D_TX1CSR2 + (((epnum) - 1) * 0x10))
#define MU3D_EP_RXCR0(epnum) (U3D_RX1CSR0 + (((epnum) - 1) * 0x10))
#define MU3D_EP_RXCR1(epnum) (U3D_RX1CSR1 + (((epnum) - 1) * 0x10))
#define MU3D_EP_RXCR2(epnum) (U3D_RX1CSR2 + (((epnum) - 1) * 0x10))
#define USB_QMU_RQCSR(epnum) (U3D_RXQCSR1 + (((epnum) - 1) * 0x10))
#define USB_QMU_RQSAR(epnum) (U3D_RXQSAR1 + (((epnum) - 1) * 0x10))
#define USB_QMU_RQCPR(epnum) (U3D_RXQCPR1 + (((epnum) - 1) * 0x10))
#define USB_QMU_TQCSR(epnum) (U3D_TXQCSR1 + (((epnum) - 1) * 0x10))
#define USB_QMU_TQSAR(epnum) (U3D_TXQSAR1 + (((epnum) - 1) * 0x10))
#define USB_QMU_TQCPR(epnum) (U3D_TXQCPR1 + (((epnum) - 1) * 0x10))
#define SSUSB_U2_CTRL(p) (U3D_SSUSB_U2_CTRL_0P + ((p) * 0x08))
#define MTU3_DRIVER_NAME "mtu3"
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#define MTU3_EP_ENABLED BIT(0)
#define MTU3_EP_STALL BIT(1)
#define MTU3_EP_WEDGE BIT(2)
#define MTU3_EP_BUSY BIT(3)
#define MTU3_U2_IP_SLOT_DEFAULT 1
/**
* Normally the device works on HS or SS, to simplify fifo management,
* devide fifo into some 512B parts, use bitmap to manage it; And
* 128 bits size of bitmap is large enough, that means it can manage
* up to 64KB fifo size.
* NOTE: MTU3_EP_FIFO_UNIT should be power of two
*/
#define MTU3_EP_FIFO_UNIT (1 << 9)
#define MTU3_FIFO_BIT_SIZE 128
#define MTU3_U2_IP_EP0_FIFO_SIZE 64
/**
* Maximum size of ep0 response buffer for ch9 requests,
* the SET_SEL request uses 6 so far, and GET_STATUS is 2
*/
#define EP0_RESPONSE_BUF 6
/* device operated link and speed got from DEVICE_CONF register */
enum mtu3_speed {
MTU3_SPEED_INACTIVE = 0,
MTU3_SPEED_FULL = 1,
MTU3_SPEED_HIGH = 3,
};
/**
* @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
* without data stage.
* @MU3D_EP0_STATE_TX: IN data stage
* @MU3D_EP0_STATE_RX: OUT data stage
* @MU3D_EP0_STATE_TX_END: the last IN data is transferred, and
* waits for its completion interrupt
* @MU3D_EP0_STATE_STALL: ep0 is in stall status, will be auto-cleared
* after receives a SETUP.
*/
enum mtu3_g_ep0_state {
MU3D_EP0_STATE_SETUP = 1,
MU3D_EP0_STATE_TX,
MU3D_EP0_STATE_RX,
MU3D_EP0_STATE_TX_END,
MU3D_EP0_STATE_STALL,
};
/**
* @base: the base address of fifo
* @limit: the bitmap size in bits
* @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
*/
struct mtu3_fifo_info {
u32 base;
u32 limit;
DECLARE_BITMAP(bitmap, MTU3_FIFO_BIT_SIZE);
};
/**
* General Purpose Descriptor (GPD):
* The format of TX GPD is a little different from RX one.
* And the size of GPD is 16 bytes.
*
* @flag:
* bit0: Hardware Own (HWO)
* bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported
* bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1
* bit7: Interrupt On Completion (IOC)
* @chksum: This is used to validate the contents of this GPD;
* If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued
* when checksum validation fails;
* Checksum value is calculated over the 16 bytes of the GPD by default;
* @data_buf_len (RX ONLY): This value indicates the length of
* the assigned data buffer
* @next_gpd: Physical address of the next GPD
* @buffer: Physical address of the data buffer
* @buf_len:
* (TX): This value indicates the length of the assigned data buffer
* (RX): The total length of data received
* @ext_len: reserved
* @ext_flag:
* bit5 (TX ONLY): Zero Length Packet (ZLP),
*/
struct qmu_gpd {
__u8 flag;
__u8 chksum;
__le16 data_buf_len;
__le32 next_gpd;
__le32 buffer;
__le16 buf_len;
__u8 ext_len;
__u8 ext_flag;
} __packed;
/**
* dma: physical base address of GPD segment
* start: virtual base address of GPD segment
* end: the last GPD element
* enqueue: the first empty GPD to use
* dequeue: the first completed GPD serviced by ISR
* NOTE: the size of GPD ring should be >= 2
*/
struct mtu3_gpd_ring {
dma_addr_t dma;
struct qmu_gpd *start;
struct qmu_gpd *end;
struct qmu_gpd *enqueue;
struct qmu_gpd *dequeue;
};
/**
* @fifo_size: it is (@slot + 1) * @fifo_seg_size
* @fifo_seg_size: it is roundup_pow_of_two(@maxp)
*/
struct mtu3_ep {
struct usb_ep ep;
char name[12];
struct mtu3 *mtu;
u8 epnum;
u8 type;
u8 is_in;
u16 maxp;
int slot;
u32 fifo_size;
u32 fifo_addr;
u32 fifo_seg_size;
struct mtu3_fifo_info *fifo;
struct list_head req_list;
struct mtu3_gpd_ring gpd_ring;
const struct usb_endpoint_descriptor *desc;
int flags;
u8 wedged;
u8 busy;
};
struct mtu3_request {
struct usb_request request;
struct list_head list;
struct mtu3_ep *mep;
struct mtu3 *mtu;
struct qmu_gpd *gpd;
int epnum;
};
/**
* struct mtu3 - device driver instance data.
* @slot: MTU3_U2_IP_SLOT_DEFAULT for U2 IP
* @may_wakeup: means device's remote wakeup is enabled
* @is_self_powered: is reported in device status and the config descriptor
* @ep0_req: dummy request used while handling standard USB requests
* for GET_STATUS and SET_SEL
* @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests
*/
struct mtu3 {
spinlock_t lock;
struct device *dev;
void __iomem *mac_base;
void __iomem *ippc_base;
struct phy *phy;
struct regulator *vusb33;
struct clk *sys_clk;
int irq;
struct mtu3_fifo_info tx_fifo;
struct mtu3_fifo_info rx_fifo;
struct mtu3_ep *ep_array;
struct mtu3_ep *in_eps;
struct mtu3_ep *out_eps;
struct mtu3_ep *ep0;
int num_eps;
int slot;
int active_ep;
struct dma_pool *qmu_gpd_pool;
enum mtu3_g_ep0_state ep0_state;
struct usb_gadget g; /* the gadget */
struct usb_gadget_driver *gadget_driver;
struct mtu3_request ep0_req;
u8 setup_buf[EP0_RESPONSE_BUF];
unsigned is_active:1;
unsigned may_wakeup:1;
unsigned is_self_powered:1;
unsigned test_mode:1;
unsigned softconnect:1;
u8 address;
u8 test_mode_nr;
u32 hw_version;
};
static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g)
{
return container_of(g, struct mtu3, g);
}
static inline int is_first_entry(const struct list_head *list,
const struct list_head *head)
{
return list_is_last(head, list);
}
static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
{
return req ? container_of(req, struct mtu3_request, request) : NULL;
}
static inline struct mtu3_ep *to_mtu3_ep(struct usb_ep *ep)
{
return ep ? container_of(ep, struct mtu3_ep, ep) : NULL;
}
static inline struct mtu3_request *next_request(struct mtu3_ep *mep)
{
struct list_head *queue = &mep->req_list;
if (list_empty(queue))
return NULL;
return list_first_entry(queue, struct mtu3_request, list);
}
static inline void mtu3_writel(void __iomem *base, u32 offset, u32 data)
{
writel(data, base + offset);
}
static inline u32 mtu3_readl(void __iomem *base, u32 offset)
{
return readl(base + offset);
}
static inline void mtu3_setbits(void __iomem *base, u32 offset, u32 bits)
{
void __iomem *addr = base + offset;
u32 tmp = readl(addr);
writel((tmp | (bits)), addr);
}
static inline void mtu3_clrbits(void __iomem *base, u32 offset, u32 bits)
{
void __iomem *addr = base + offset;
u32 tmp = readl(addr);
writel((tmp & ~(bits)), addr);
}
int ssusb_check_clocks(struct mtu3 *mtu, u32 ex_clks);
struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
void mtu3_free_request(struct usb_ep *ep, struct usb_request *req);
void mtu3_req_complete(struct mtu3_ep *mep,
struct usb_request *req, int status);
int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
int interval, int burst, int mult);
void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
void mtu3_ep0_setup(struct mtu3 *mtu);
void mtu3_start(struct mtu3 *mtu);
void mtu3_stop(struct mtu3 *mtu);
void mtu3_hs_softconn_set(struct mtu3 *mtu, bool enable);
int mtu3_gadget_setup(struct mtu3 *mtu);
void mtu3_gadget_cleanup(struct mtu3 *mtu);
void mtu3_gadget_reset(struct mtu3 *mtu);
void mtu3_gadget_suspend(struct mtu3 *mtu);
void mtu3_gadget_resume(struct mtu3 *mtu);
void mtu3_gadget_disconnect(struct mtu3 *mtu);
int ssusb_gadget_init(struct mtu3 *mtu);
void ssusb_gadget_exit(struct mtu3 *mtu);
irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu);
extern const struct usb_ep_ops mtu3_ep0_ops;
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+440
View File
@@ -0,0 +1,440 @@
/*
* mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
*
* Copyright (C) 2016 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _SSUSB_HW_REGS_H_
#define _SSUSB_HW_REGS_H_
/* segment offset of MAC register */
#define SSUSB_DEV_BASE 0x0000
#define SSUSB_EPCTL_CSR_BASE 0x0800
#define SSUSB_USB3_MAC_CSR_BASE 0x1400
#define SSUSB_USB3_SYS_CSR_BASE 0x1400
#define SSUSB_USB2_CSR_BASE 0x2400
/* IPPC register in Infra */
#define SSUSB_SIFSLV_IPPC_BASE 0x0000
/* --------------- SSUSB_DEV REGISTER DEFINITION --------------- */
#define U3D_LV1ISR (SSUSB_DEV_BASE + 0x0000)
#define U3D_LV1IER (SSUSB_DEV_BASE + 0x0004)
#define U3D_LV1IESR (SSUSB_DEV_BASE + 0x0008)
#define U3D_LV1IECR (SSUSB_DEV_BASE + 0x000C)
#define U3D_EPISR (SSUSB_DEV_BASE + 0x0080)
#define U3D_EPIER (SSUSB_DEV_BASE + 0x0084)
#define U3D_EPIESR (SSUSB_DEV_BASE + 0x0088)
#define U3D_EPIECR (SSUSB_DEV_BASE + 0x008C)
#define U3D_EP0CSR (SSUSB_DEV_BASE + 0x0100)
#define U3D_RXCOUNT0 (SSUSB_DEV_BASE + 0x0108)
#define U3D_RESERVED (SSUSB_DEV_BASE + 0x010C)
#define U3D_TX1CSR0 (SSUSB_DEV_BASE + 0x0110)
#define U3D_TX1CSR1 (SSUSB_DEV_BASE + 0x0114)
#define U3D_TX1CSR2 (SSUSB_DEV_BASE + 0x0118)
#define U3D_RX1CSR0 (SSUSB_DEV_BASE + 0x0210)
#define U3D_RX1CSR1 (SSUSB_DEV_BASE + 0x0214)
#define U3D_RX1CSR2 (SSUSB_DEV_BASE + 0x0218)
#define U3D_FIFO0 (SSUSB_DEV_BASE + 0x0300)
#define U3D_QCR0 (SSUSB_DEV_BASE + 0x0400)
#define U3D_QCR1 (SSUSB_DEV_BASE + 0x0404)
#define U3D_QCR2 (SSUSB_DEV_BASE + 0x0408)
#define U3D_QCR3 (SSUSB_DEV_BASE + 0x040C)
#define U3D_TXQCSR1 (SSUSB_DEV_BASE + 0x0510)
#define U3D_TXQSAR1 (SSUSB_DEV_BASE + 0x0514)
#define U3D_TXQCPR1 (SSUSB_DEV_BASE + 0x0518)
#define U3D_RXQCSR1 (SSUSB_DEV_BASE + 0x0610)
#define U3D_RXQSAR1 (SSUSB_DEV_BASE + 0x0614)
#define U3D_RXQCPR1 (SSUSB_DEV_BASE + 0x0618)
#define U3D_RXQLDPR1 (SSUSB_DEV_BASE + 0x061C)
#define U3D_QISAR0 (SSUSB_DEV_BASE + 0x0700)
#define U3D_QIER0 (SSUSB_DEV_BASE + 0x0704)
#define U3D_QIESR0 (SSUSB_DEV_BASE + 0x0708)
#define U3D_QIECR0 (SSUSB_DEV_BASE + 0x070C)
#define U3D_QISAR1 (SSUSB_DEV_BASE + 0x0710)
#define U3D_QIER1 (SSUSB_DEV_BASE + 0x0714)
#define U3D_QIESR1 (SSUSB_DEV_BASE + 0x0718)
#define U3D_QIECR1 (SSUSB_DEV_BASE + 0x071C)
#define U3D_TQERRIR0 (SSUSB_DEV_BASE + 0x0780)
#define U3D_TQERRIER0 (SSUSB_DEV_BASE + 0x0784)
#define U3D_TQERRIESR0 (SSUSB_DEV_BASE + 0x0788)
#define U3D_TQERRIECR0 (SSUSB_DEV_BASE + 0x078C)
#define U3D_RQERRIR0 (SSUSB_DEV_BASE + 0x07C0)
#define U3D_RQERRIER0 (SSUSB_DEV_BASE + 0x07C4)
#define U3D_RQERRIESR0 (SSUSB_DEV_BASE + 0x07C8)
#define U3D_RQERRIECR0 (SSUSB_DEV_BASE + 0x07CC)
#define U3D_RQERRIR1 (SSUSB_DEV_BASE + 0x07D0)
#define U3D_RQERRIER1 (SSUSB_DEV_BASE + 0x07D4)
#define U3D_RQERRIESR1 (SSUSB_DEV_BASE + 0x07D8)
#define U3D_RQERRIECR1 (SSUSB_DEV_BASE + 0x07DC)
#define U3D_CAP_EP0FFSZ (SSUSB_DEV_BASE + 0x0C04)
#define U3D_CAP_EPNTXFFSZ (SSUSB_DEV_BASE + 0x0C08)
#define U3D_CAP_EPNRXFFSZ (SSUSB_DEV_BASE + 0x0C0C)
#define U3D_CAP_EPINFO (SSUSB_DEV_BASE + 0x0C10)
#define U3D_MISC_CTRL (SSUSB_DEV_BASE + 0x0C84)
/*---------------- SSUSB_DEV FIELD DEFINITION ---------------*/
/* U3D_LV1ISR */
#define EP_CTRL_INTR BIT(5)
#define MAC2_INTR BIT(4)
#define DMA_INTR BIT(3)
#define MAC3_INTR BIT(2)
#define QMU_INTR BIT(1)
#define BMU_INTR BIT(0)
/* U3D_LV1IECR */
#define LV1IECR_MSK GENMASK(31, 0)
/* U3D_EPISR */
#define EPRISR(x) (BIT(16) << (x))
#define EPTISR(x) (BIT(0) << (x))
#define EP0ISR BIT(0)
/* U3D_EP0CSR */
#define EP0_SENDSTALL BIT(25)
#define EP0_FIFOFULL BIT(23)
#define EP0_SENTSTALL BIT(22)
#define EP0_DPHTX BIT(20)
#define EP0_DATAEND BIT(19)
#define EP0_TXPKTRDY BIT(18)
#define EP0_SETUPPKTRDY BIT(17)
#define EP0_RXPKTRDY BIT(16)
#define EP0_MAXPKTSZ_MSK GENMASK(9, 0)
#define EP0_MAXPKTSZ(x) ((x) & EP0_MAXPKTSZ_MSK)
#define EP0_W1C_BITS (~(EP0_RXPKTRDY | EP0_SETUPPKTRDY | EP0_SENTSTALL))
/* U3D_TX1CSR0 */
#define TX_DMAREQEN BIT(29)
#define TX_FIFOFULL BIT(25)
#define TX_FIFOEMPTY BIT(24)
#define TX_SENTSTALL BIT(22)
#define TX_SENDSTALL BIT(21)
#define TX_TXPKTRDY BIT(16)
#define TX_TXMAXPKTSZ_MSK GENMASK(10, 0)
#define TX_TXMAXPKTSZ(x) ((x) & TX_TXMAXPKTSZ_MSK)
#define TX_W1C_BITS (~(TX_SENTSTALL))
/* U3D_TX1CSR1 */
#define TX_MULT(x) (((x) & 0x3) << 22)
#define TX_MAX_PKT(x) (((x) & 0x3f) << 16)
#define TX_SLOT(x) (((x) & 0x3f) << 8)
#define TX_TYPE(x) (((x) & 0x3) << 4)
#define TX_SS_BURST(x) (((x) & 0xf) << 0)
/* for TX_TYPE & RX_TYPE */
#define TYPE_BULK (0x0)
#define TYPE_INT (0x1)
#define TYPE_ISO (0x2)
#define TYPE_MASK (0x3)
/* U3D_TX1CSR2 */
#define TX_BINTERVAL(x) (((x) & 0xff) << 24)
#define TX_FIFOSEGSIZE(x) (((x) & 0xf) << 16)
#define TX_FIFOADDR(x) (((x) & 0x1fff) << 0)
/* U3D_RX1CSR0 */
#define RX_DMAREQEN BIT(29)
#define RX_SENTSTALL BIT(22)
#define RX_SENDSTALL BIT(21)
#define RX_RXPKTRDY BIT(16)
#define RX_RXMAXPKTSZ_MSK GENMASK(10, 0)
#define RX_RXMAXPKTSZ(x) ((x) & RX_RXMAXPKTSZ_MSK)
#define RX_W1C_BITS (~(RX_SENTSTALL | RX_RXPKTRDY))
/* U3D_RX1CSR1 */
#define RX_MULT(x) (((x) & 0x3) << 22)
#define RX_MAX_PKT(x) (((x) & 0x3f) << 16)
#define RX_SLOT(x) (((x) & 0x3f) << 8)
#define RX_TYPE(x) (((x) & 0x3) << 4)
#define RX_SS_BURST(x) (((x) & 0xf) << 0)
/* U3D_RX1CSR2 */
#define RX_BINTERVAL(x) (((x) & 0xff) << 24)
#define RX_FIFOSEGSIZE(x) (((x) & 0xf) << 16)
#define RX_FIFOADDR(x) (((x) & 0x1fff) << 0)
/* U3D_QCR0 */
#define QMU_RX_CS_EN(x) (BIT(16) << (x))
#define QMU_TX_CS_EN(x) (BIT(0) << (x))
#define QMU_CS16B_EN BIT(0)
/* U3D_QCR1 */
#define QMU_TX_ZLP(x) (BIT(0) << (x))
/* U3D_QCR3 */
#define QMU_RX_COZ(x) (BIT(16) << (x))
#define QMU_RX_ZLP(x) (BIT(0) << (x))
/* U3D_TXQCSR1 */
/* U3D_RXQCSR1 */
#define QMU_Q_ACTIVE BIT(15)
#define QMU_Q_STOP BIT(2)
#define QMU_Q_RESUME BIT(1)
#define QMU_Q_START BIT(0)
/* U3D_QISAR0, U3D_QIER0, U3D_QIESR0, U3D_QIECR0 */
#define QMU_RX_DONE_INT(x) (BIT(16) << (x))
#define QMU_TX_DONE_INT(x) (BIT(0) << (x))
/* U3D_QISAR1, U3D_QIER1, U3D_QIESR1, U3D_QIECR1 */
#define RXQ_ZLPERR_INT BIT(20)
#define RXQ_LENERR_INT BIT(18)
#define RXQ_CSERR_INT BIT(17)
#define RXQ_EMPTY_INT BIT(16)
#define TXQ_LENERR_INT BIT(2)
#define TXQ_CSERR_INT BIT(1)
#define TXQ_EMPTY_INT BIT(0)
/* U3D_TQERRIR0, U3D_TQERRIER0, U3D_TQERRIESR0, U3D_TQERRIECR0 */
#define QMU_TX_LEN_ERR(x) (BIT(16) << (x))
#define QMU_TX_CS_ERR(x) (BIT(0) << (x))
/* U3D_RQERRIR0, U3D_RQERRIER0, U3D_RQERRIESR0, U3D_RQERRIECR0 */
#define QMU_RX_LEN_ERR(x) (BIT(16) << (x))
#define QMU_RX_CS_ERR(x) (BIT(0) << (x))
/* U3D_RQERRIR1, U3D_RQERRIER1, U3D_RQERRIESR1, U3D_RQERRIECR1 */
#define QMU_RX_ZLP_ERR(n) (BIT(16) << (n))
/* U3D_CAP_EPINFO */
#define CAP_RX_EP_NUM(x) (((x) >> 8) & 0x1f)
#define CAP_TX_EP_NUM(x) ((x) & 0x1f)
/* U3D_MISC_CTRL */
#define VBUS_ON BIT(1)
#define VBUS_FRC_EN BIT(0)
/*---------------- SSUSB_EPCTL_CSR REGISTER DEFINITION ----------------*/
#define U3D_DEVICE_CONF (SSUSB_EPCTL_CSR_BASE + 0x0000)
#define U3D_EP_RST (SSUSB_EPCTL_CSR_BASE + 0x0004)
#define U3D_DEV_LINK_INTR_ENABLE (SSUSB_EPCTL_CSR_BASE + 0x0050)
#define U3D_DEV_LINK_INTR (SSUSB_EPCTL_CSR_BASE + 0x0054)
/*---------------- SSUSB_EPCTL_CSR FIELD DEFINITION ----------------*/
/* U3D_DEVICE_CONF */
#define DEV_ADDR_MSK GENMASK(30, 24)
#define DEV_ADDR(x) ((0x7f & (x)) << 24)
#define HW_USB2_3_SEL BIT(18)
#define SW_USB2_3_SEL_EN BIT(17)
#define SW_USB2_3_SEL BIT(16)
#define SSUSB_DEV_SPEED(x) ((x) & 0x7)
/* U3D_EP_RST */
#define EP1_IN_RST BIT(17)
#define EP1_OUT_RST BIT(1)
#define EP_RST(is_in, epnum) (((is_in) ? BIT(16) : BIT(0)) << (epnum))
#define EP0_RST BIT(0)
/* U3D_DEV_LINK_INTR_ENABLE */
/* U3D_DEV_LINK_INTR */
#define SSUSB_DEV_SPEED_CHG_INTR BIT(0)
/*---------------- SSUSB_USB3_MAC_CSR REGISTER DEFINITION ----------------*/
#define U3D_USB3_CONFIG (SSUSB_USB3_MAC_CSR_BASE + 0x001C)
/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
/* U3D_USB3_CONFIG */
#define USB3_EN BIT(0)
/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
#define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C)
#define U3D_LINK_POWER_CONTROL (SSUSB_USB3_SYS_CSR_BASE + 0x0210)
#define U3D_LINK_ERR_COUNT (SSUSB_USB3_SYS_CSR_BASE + 0x0214)
/*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/
/* U3D_LINK_UX_INACT_TIMER */
#define DEV_U2_INACT_TIMEOUT_MSK GENMASK(23, 16)
#define DEV_U2_INACT_TIMEOUT_VALUE(x) (((x) & 0xff) << 16)
#define U2_INACT_TIMEOUT_MSK GENMASK(15, 8)
#define U1_INACT_TIMEOUT_MSK GENMASK(7, 0)
#define U1_INACT_TIMEOUT_VALUE(x) ((x) & 0xff)
/* U3D_LINK_POWER_CONTROL */
#define SW_U2_ACCEPT_ENABLE BIT(9)
#define SW_U1_ACCEPT_ENABLE BIT(8)
#define UX_EXIT BIT(5)
#define LGO_U3 BIT(4)
#define LGO_U2 BIT(3)
#define LGO_U1 BIT(2)
#define SW_U2_REQUEST_ENABLE BIT(1)
#define SW_U1_REQUEST_ENABLE BIT(0)
/* U3D_LINK_ERR_COUNT */
#define CLR_LINK_ERR_CNT BIT(16)
#define LINK_ERROR_COUNT GENMASK(15, 0)
/*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/
#define U3D_POWER_MANAGEMENT (SSUSB_USB2_CSR_BASE + 0x0004)
#define U3D_DEVICE_CONTROL (SSUSB_USB2_CSR_BASE + 0x000C)
#define U3D_USB2_TEST_MODE (SSUSB_USB2_CSR_BASE + 0x0014)
#define U3D_COMMON_USB_INTR_ENABLE (SSUSB_USB2_CSR_BASE + 0x0018)
#define U3D_COMMON_USB_INTR (SSUSB_USB2_CSR_BASE + 0x001C)
#define U3D_LINK_RESET_INFO (SSUSB_USB2_CSR_BASE + 0x0024)
#define U3D_USB20_FRAME_NUM (SSUSB_USB2_CSR_BASE + 0x003C)
#define U3D_USB20_LPM_PARAMETER (SSUSB_USB2_CSR_BASE + 0x0044)
#define U3D_USB20_MISC_CONTROL (SSUSB_USB2_CSR_BASE + 0x004C)
/*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/
/* U3D_POWER_MANAGEMENT */
#define LPM_BESL_STALL BIT(14)
#define LPM_BESLD_STALL BIT(13)
#define LPM_RWP BIT(11)
#define LPM_HRWE BIT(10)
#define LPM_MODE(x) (((x) & 0x3) << 8)
#define ISO_UPDATE BIT(7)
#define SOFT_CONN BIT(6)
#define HS_ENABLE BIT(5)
#define RESUME BIT(2)
#define SUSPENDM_ENABLE BIT(0)
/* U3D_DEVICE_CONTROL */
#define DC_HOSTREQ BIT(1)
#define DC_SESSION BIT(0)
/* U3D_USB2_TEST_MODE */
#define U2U3_AUTO_SWITCH BIT(10)
#define LPM_FORCE_STALL BIT(8)
#define FIFO_ACCESS BIT(6)
#define FORCE_FS BIT(5)
#define FORCE_HS BIT(4)
#define TEST_PACKET_MODE BIT(3)
#define TEST_K_MODE BIT(2)
#define TEST_J_MODE BIT(1)
#define TEST_SE0_NAK_MODE BIT(0)
/* U3D_COMMON_USB_INTR_ENABLE */
/* U3D_COMMON_USB_INTR */
#define LPM_RESUME_INTR BIT(9)
#define LPM_INTR BIT(8)
#define DISCONN_INTR BIT(5)
#define CONN_INTR BIT(4)
#define SOF_INTR BIT(3)
#define RESET_INTR BIT(2)
#define RESUME_INTR BIT(1)
#define SUSPEND_INTR BIT(0)
/* U3D_LINK_RESET_INFO */
#define WTCHRP_MSK GENMASK(19, 16)
/* U3D_USB20_LPM_PARAMETER */
#define LPM_BESLCK_U3(x) (((x) & 0xf) << 12)
#define LPM_BESLCK(x) (((x) & 0xf) << 8)
#define LPM_BESLDCK(x) (((x) & 0xf) << 4)
#define LPM_BESL GENMASK(3, 0)
/* U3D_USB20_MISC_CONTROL */
#define LPM_U3_ACK_EN BIT(0)
/*---------------- SSUSB_SIFSLV_IPPC REGISTER DEFINITION ----------------*/
#define U3D_SSUSB_IP_PW_CTRL0 (SSUSB_SIFSLV_IPPC_BASE + 0x0000)
#define U3D_SSUSB_IP_PW_CTRL1 (SSUSB_SIFSLV_IPPC_BASE + 0x0004)
#define U3D_SSUSB_IP_PW_CTRL2 (SSUSB_SIFSLV_IPPC_BASE + 0x0008)
#define U3D_SSUSB_IP_PW_CTRL3 (SSUSB_SIFSLV_IPPC_BASE + 0x000C)
#define U3D_SSUSB_IP_PW_STS1 (SSUSB_SIFSLV_IPPC_BASE + 0x0010)
#define U3D_SSUSB_IP_PW_STS2 (SSUSB_SIFSLV_IPPC_BASE + 0x0014)
#define U3D_SSUSB_OTG_STS (SSUSB_SIFSLV_IPPC_BASE + 0x0018)
#define U3D_SSUSB_OTG_STS_CLR (SSUSB_SIFSLV_IPPC_BASE + 0x001C)
#define U3D_SSUSB_IP_XHCI_CAP (SSUSB_SIFSLV_IPPC_BASE + 0x0024)
#define U3D_SSUSB_IP_DEV_CAP (SSUSB_SIFSLV_IPPC_BASE + 0x0028)
#define U3D_SSUSB_OTG_INT_EN (SSUSB_SIFSLV_IPPC_BASE + 0x002C)
#define U3D_SSUSB_U3_CTRL_0P (SSUSB_SIFSLV_IPPC_BASE + 0x0030)
#define U3D_SSUSB_U2_CTRL_0P (SSUSB_SIFSLV_IPPC_BASE + 0x0050)
#define U3D_SSUSB_REF_CK_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x008C)
#define U3D_SSUSB_DEV_RST_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x0098)
#define U3D_SSUSB_HW_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A0)
#define U3D_SSUSB_HW_SUB_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A4)
#define U3D_SSUSB_IP_SPARE0 (SSUSB_SIFSLV_IPPC_BASE + 0x00C8)
/*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/
/* U3D_SSUSB_IP_PW_CTRL0 */
#define SSUSB_IP_SW_RST BIT(0)
/* U3D_SSUSB_IP_PW_CTRL1 */
#define SSUSB_IP_HOST_PDN BIT(0)
/* U3D_SSUSB_IP_PW_CTRL2 */
#define SSUSB_IP_DEV_PDN BIT(0)
/* U3D_SSUSB_IP_PW_CTRL3 */
#define SSUSB_IP_PCIE_PDN BIT(0)
/* U3D_SSUSB_IP_PW_STS1 */
#define SSUSB_IP_SLEEP_STS BIT(30)
#define SSUSB_U3_MAC_RST_B_STS BIT(16)
#define SSUSB_XHCI_RST_B_STS BIT(11)
#define SSUSB_SYS125_RST_B_STS BIT(10)
#define SSUSB_REF_RST_B_STS BIT(8)
#define SSUSB_SYSPLL_STABLE BIT(0)
/* U3D_SSUSB_IP_PW_STS2 */
#define SSUSB_U2_MAC_SYS_RST_B_STS BIT(0)
/* U3D_SSUSB_OTG_STS */
#define SSUSB_VBUS_VALID BIT(9)
/* U3D_SSUSB_OTG_STS_CLR */
#define SSUSB_VBUS_INTR_CLR BIT(6)
/* U3D_SSUSB_IP_XHCI_CAP */
#define SSUSB_IP_XHCI_U2_PORT_NUM(x) (((x) >> 8) & 0xff)
#define SSUSB_IP_XHCI_U3_PORT_NUM(x) ((x) & 0xff)
/* U3D_SSUSB_IP_DEV_CAP */
#define SSUSB_IP_DEV_U3_PORT_NUM(x) ((x) & 0xff)
/* U3D_SSUSB_OTG_INT_EN */
#define SSUSB_VBUS_CHG_INT_A_EN BIT(7)
#define SSUSB_VBUS_CHG_INT_B_EN BIT(6)
/* U3D_SSUSB_U3_CTRL_0P */
#define SSUSB_U3_PORT_HOST_SEL BIT(2)
#define SSUSB_U3_PORT_PDN BIT(1)
#define SSUSB_U3_PORT_DIS BIT(0)
/* U3D_SSUSB_U2_CTRL_0P */
#define SSUSB_U2_PORT_OTG_SEL BIT(7)
#define SSUSB_U2_PORT_HOST_SEL BIT(2)
#define SSUSB_U2_PORT_PDN BIT(1)
#define SSUSB_U2_PORT_DIS BIT(0)
/* U3D_SSUSB_DEV_RST_CTRL */
#define SSUSB_DEV_SW_RST BIT(0)
#endif /* _SSUSB_HW_REGS_H_ */
+251
View File
@@ -0,0 +1,251 @@
/*
* Copyright (C) 2016 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include "mtu3.h"
/* u2-port0 should be powered on and enabled; */
int ssusb_check_clocks(struct mtu3 *mtu, u32 ex_clks)
{
void __iomem *ibase = mtu->ippc_base;
u32 value, check_val;
int ret;
check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
SSUSB_REF_RST_B_STS;
ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
(check_val == (value & check_val)), 100, 20000);
if (ret) {
dev_err(mtu->dev, "clks of sts1 are not stable!\n");
return ret;
}
ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
(value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000);
if (ret) {
dev_err(mtu->dev, "mac2 clock is not stable\n");
return ret;
}
return 0;
}
static int ssusb_rscs_init(struct mtu3 *mtu)
{
int ret = 0;
ret = regulator_enable(mtu->vusb33);
if (ret) {
dev_err(mtu->dev, "failed to enable vusb33\n");
goto vusb33_err;
}
ret = clk_prepare_enable(mtu->sys_clk);
if (ret) {
dev_err(mtu->dev, "failed to enable sys_clk\n");
goto clk_err;
}
ret = phy_init(mtu->phy);
if (ret) {
dev_err(mtu->dev, "failed to init phy\n");
goto phy_init_err;
}
ret = phy_power_on(mtu->phy);
if (ret) {
dev_err(mtu->dev, "failed to power on phy\n");
goto phy_err;
}
return 0;
phy_err:
phy_exit(mtu->phy);
phy_init_err:
clk_disable_unprepare(mtu->sys_clk);
clk_err:
regulator_disable(mtu->vusb33);
vusb33_err:
return ret;
}
static void ssusb_rscs_exit(struct mtu3 *mtu)
{
clk_disable_unprepare(mtu->sys_clk);
regulator_disable(mtu->vusb33);
phy_power_off(mtu->phy);
phy_exit(mtu->phy);
}
static void ssusb_ip_sw_reset(struct mtu3 *mtu)
{
mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
udelay(1);
mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
}
static int get_ssusb_rscs(struct platform_device *pdev, struct mtu3 *mtu)
{
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct resource *res;
mtu->phy = devm_of_phy_get_by_index(dev, node, 0);
if (IS_ERR(mtu->phy)) {
dev_err(dev, "failed to get phy\n");
return PTR_ERR(mtu->phy);
}
mtu->irq = platform_get_irq(pdev, 0);
if (mtu->irq <= 0) {
dev_err(dev, "fail to get irq number\n");
return -ENODEV;
}
dev_info(dev, "irq %d\n", mtu->irq);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
mtu->mac_base = devm_ioremap_resource(dev, res);
if (IS_ERR(mtu->mac_base)) {
dev_err(dev, "error mapping memory for dev mac\n");
return PTR_ERR(mtu->mac_base);
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
mtu->ippc_base = devm_ioremap_resource(dev, res);
if (IS_ERR(mtu->ippc_base)) {
dev_err(dev, "failed to map memory for ippc\n");
return PTR_ERR(mtu->ippc_base);
}
mtu->vusb33 = devm_regulator_get(&pdev->dev, "vusb33");
if (IS_ERR(mtu->vusb33)) {
dev_err(dev, "failed to get vusb33\n");
return PTR_ERR(mtu->vusb33);
}
mtu->sys_clk = devm_clk_get(dev, "sys_ck");
if (IS_ERR(mtu->sys_clk)) {
dev_err(dev, "failed to get sys clock\n");
return PTR_ERR(mtu->sys_clk);
}
return 0;
}
static int mtu3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtu3 *mtu;
int ret = -ENOMEM;
/* all elements are set to ZERO as default value */
mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL);
if (!mtu)
return -ENOMEM;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "No suitable DMA config available\n");
return -ENOTSUPP;
}
platform_set_drvdata(pdev, mtu);
mtu->dev = dev;
spin_lock_init(&mtu->lock);
ret = get_ssusb_rscs(pdev, mtu);
if (ret)
return ret;
/* enable power domain */
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
device_enable_async_suspend(dev);
ret = ssusb_rscs_init(mtu);
if (ret)
goto comm_init_err;
ssusb_ip_sw_reset(mtu);
ret = ssusb_gadget_init(mtu);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
goto comm_exit;
}
return 0;
comm_exit:
ssusb_rscs_exit(mtu);
comm_init_err:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
return ret;
}
static int mtu3_remove(struct platform_device *pdev)
{
struct mtu3 *mtu = platform_get_drvdata(pdev);
ssusb_gadget_exit(mtu);
ssusb_rscs_exit(mtu);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id mtu3_of_match[] = {
{.compatible = "mediatek,mt8173-mtu3",},
{},
};
MODULE_DEVICE_TABLE(of, mtu3_of_match);
#endif
static struct platform_driver mtu3_driver = {
.probe = mtu3_probe,
.remove = mtu3_remove,
.driver = {
.name = MTU3_DRIVER_NAME,
.of_match_table = of_match_ptr(mtu3_of_match),
},
};
module_platform_driver(mtu3_driver);
MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver");
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
/*
* mtu3_qmu.h - Queue Management Unit driver header
*
* Copyright (C) 2016 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __MTK_QMU_H__
#define __MTK_QMU_H__
#define MAX_GPD_NUM 64
#define QMU_GPD_SIZE (sizeof(struct qmu_gpd))
#define QMU_GPD_RING_SIZE (MAX_GPD_NUM * QMU_GPD_SIZE)
#define GPD_BUF_SIZE 65532
void mtu3_qmu_stop(struct mtu3_ep *mep);
int mtu3_qmu_start(struct mtu3_ep *mep);
void mtu3_qmu_resume(struct mtu3_ep *mep);
void mtu3_qmu_flush(struct mtu3_ep *mep);
void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq);
int mtu3_prepare_transfer(struct mtu3_ep *mep);
int mtu3_gpd_ring_alloc(struct mtu3_ep *mep);
void mtu3_gpd_ring_free(struct mtu3_ep *mep);
irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu);
int mtu3_qmu_init(struct mtu3 *mtu);
void mtu3_qmu_exit(struct mtu3 *mtu);
#endif