Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Intel Langwell USB Device Controller driver
 * Copyright (C) 2008-2009, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope 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.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include <linux/usb/langwell_udc.h>
#include <linux/usb/langwell_otg.h>

/*-------------------------------------------------------------------------*/

/* driver data structures and utilities */

/*
 * dTD: Device Endpoint Transfer Descriptor
 * describe to the device controller the location and quantity of
 * data to be send/received for given transfer
 */
struct langwell_dtd {
	u32	dtd_next;
/* bits 31:5, next transfer element pointer */
#define	DTD_NEXT(d)	(((d)>>5)&0x7ffffff)
#define	DTD_NEXT_MASK	(0x7ffffff << 5)
/* terminate */
#define	DTD_TERM	BIT(0)
	/* bits 7:0, execution back states */
	u32	dtd_status:8;
#define	DTD_STATUS(d)	(((d)>>0)&0xff)
#define	DTD_STS_ACTIVE	BIT(7)	/* active */
#define	DTD_STS_HALTED	BIT(6)	/* halted */
#define	DTD_STS_DBE	BIT(5)	/* data buffer error */
#define	DTD_STS_TRE	BIT(3)	/* transaction error  */
	/* bits 9:8 */
	u32	dtd_res0:2;
	/* bits 11:10, multipier override */
	u32	dtd_multo:2;
#define	DTD_MULTO	(BIT(11) | BIT(10))
	/* bits 14:12 */
	u32	dtd_res1:3;
	/* bit 15, interrupt on complete */
	u32	dtd_ioc:1;
#define	DTD_IOC		BIT(15)
	/* bits 30:16, total bytes */
	u32	dtd_total:15;
#define	DTD_TOTAL(d)	(((d)>>16)&0x7fff)
#define	DTD_MAX_TRANSFER_LENGTH	0x4000
	/* bit 31 */
	u32	dtd_res2:1;
	/* dTD buffer pointer page 0 to 4 */
	u32	dtd_buf[5];
#define	DTD_OFFSET_MASK	0xfff
/* bits 31:12, buffer pointer */
#define	DTD_BUFFER(d)	(((d)>>12)&0x3ff)
/* bits 11:0, current offset */
#define	DTD_C_OFFSET(d)	(((d)>>0)&0xfff)
/* bits 10:0, frame number */
#define	DTD_FRAME(d)	(((d)>>0)&0x7ff)

	/* driver-private parts */

	/* dtd dma address */
	dma_addr_t		dtd_dma;
	/* next dtd virtual address */
	struct langwell_dtd	*next_dtd_virt;
};


/*
 * dQH: Device Endpoint Queue Head
 * describe where all transfers are managed
 * 48-byte data structure, aligned on 64-byte boundary
 *
 * These are associated with dTD structure
 */
struct langwell_dqh {
	/* endpoint capabilities and characteristics */
	u32	dqh_res0:15;	/* bits 14:0 */
	u32	dqh_ios:1;	/* bit 15, interrupt on setup */
#define	DQH_IOS		BIT(15)
	u32	dqh_mpl:11;	/* bits 26:16, maximum packet length */
#define	DQH_MPL		(0x7ff << 16)
	u32	dqh_res1:2;	/* bits 28:27 */
	u32	dqh_zlt:1;	/* bit 29, zero length termination */
#define	DQH_ZLT		BIT(29)
	u32	dqh_mult:2;	/* bits 31:30 */
#define	DQH_MULT	(BIT(30) | BIT(31))

	/* current dTD pointer */
	u32	dqh_current;	/* locate the transfer in progress */
#define DQH_C_DTD(e)	\
	(((e)>>5)&0x7ffffff)	/* bits 31:5, current dTD pointer */

	/* transfer overlay, hardware parts of a struct langwell_dtd */
	u32	dtd_next;
	u32	dtd_status:8;	/* bits 7:0, execution back states */
	u32	dtd_res0:2;	/* bits 9:8 */
	u32	dtd_multo:2;	/* bits 11:10, multipier override */
	u32	dtd_res1:3;	/* bits 14:12 */
	u32	dtd_ioc:1;	/* bit 15, interrupt on complete */
	u32	dtd_total:15;	/* bits 30:16, total bytes */
	u32	dtd_res2:1;	/* bit 31 */
	u32	dtd_buf[5];	/* dTD buffer pointer page 0 to 4 */

	u32	dqh_res2;
	struct usb_ctrlrequest	dqh_setup;	/* setup packet buffer */
} __attribute__ ((aligned(64)));


/* endpoint data structure */
struct langwell_ep {
	struct usb_ep		ep;
	dma_addr_t		dma;
	struct langwell_udc	*dev;
	unsigned long		irqs;
	struct list_head	queue;
	struct langwell_dqh	*dqh;
	const struct usb_endpoint_descriptor	*desc;
	char			name[14];
	unsigned		stopped:1,
				ep_type:2,
				ep_num:8;
};


/* request data structure */
struct langwell_request {
	struct usb_request	req;
	struct langwell_dtd	*dtd, *head, *tail;
	struct langwell_ep	*ep;
	dma_addr_t		dtd_dma;
	struct list_head	queue;
	unsigned		dtd_count;
	unsigned		mapped:1;
};


/* ep0 transfer state */
enum ep0_state {
	WAIT_FOR_SETUP,
	DATA_STATE_XMIT,
	DATA_STATE_NEED_ZLP,
	WAIT_FOR_OUT_STATUS,
	DATA_STATE_RECV,
};


/* device suspend state */
enum lpm_state {
	LPM_L0,	/* on */
	LPM_L1,	/* LPM L1 sleep */
	LPM_L2,	/* suspend */
	LPM_L3,	/* off */
};


/* device data structure */
struct langwell_udc {
	/* each pci device provides one gadget, several endpoints */
	struct usb_gadget	gadget;
	spinlock_t		lock;	/* device lock */
	struct langwell_ep	*ep;
	struct usb_gadget_driver	*driver;
	struct otg_transceiver	*transceiver;
	u8			dev_addr;
	u32			usb_state;
	u32			resume_state;
	u32			bus_reset;
	enum lpm_state		lpm_state;
	enum ep0_state		ep0_state;
	u32			ep0_dir;
	u16			dciversion;
	unsigned		ep_max;
	unsigned		devcap:1,
				enabled:1,
				region:1,
				got_irq:1,
				powered:1,
				remote_wakeup:1,
				rate:1,
				is_reset:1,
				softconnected:1,
				vbus_active:1,
				suspended:1,
				stopped:1,
				lpm:1,		/* LPM capability */
				has_sram:1,	/* SRAM caching */
				got_sram:1;

	/* pci state used to access those endpoints */
	struct pci_dev		*pdev;

	/* Langwell otg transceiver */
	struct langwell_otg	*lotg;

	/* control registers */
	struct langwell_cap_regs	__iomem	*cap_regs;
	struct langwell_op_regs		__iomem	*op_regs;

	struct usb_ctrlrequest	local_setup_buff;
	struct langwell_dqh	*ep_dqh;
	size_t			ep_dqh_size;
	dma_addr_t		ep_dqh_dma;

	/* ep0 status request */
	struct langwell_request	*status_req;

	/* dma pool */
	struct dma_pool		*dtd_pool;

	/* make sure release() is done */
	struct completion	*done;

	/* for private SRAM caching */
	unsigned int		sram_addr;
	unsigned int		sram_size;

	/* device status data for get_status request */
	u16			dev_status;
};