Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51e2f62f authored by Jan Andersson's avatar Jan Andersson Committed by Greg Kroah-Hartman
Browse files

USB: UHCI: Add support for big endian descriptors



This patch adds support for universal host controllers that use
big endian descriptors. Support for BE descriptors requires a non-PCI
host controller. For kernels with PCI-only UHCI there should be no
change in behaviour.

This patch tries to replicate the technique used to support BE descriptors
in the EHCI HCD. Parts added to uhci-hcd.h are basically copy'n'paste from
ehci.h.

Signed-off-by: default avatarJan Andersson <jan@gaisler.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent bab1ff1b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -420,6 +420,10 @@ config USB_UHCI_BIG_ENDIAN_MMIO
	bool
	depends on USB_UHCI_SUPPORT_NON_PCI_HC

config USB_UHCI_BIG_ENDIAN_DESC
	bool
	depends on USB_UHCI_SUPPORT_NON_PCI_HC

config USB_FHCI_HCD
	tristate "Freescale QE USB Host Controller support"
	depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE
+39 −32
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ static void lprintk(char *buf)
	}
}

static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf,
			int len, int space)
{
	char *out = buf;
	char *spid;
@@ -47,8 +48,9 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
	if (len < 160)
		return 0;

	status = td_status(td);
	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
	status = td_status(uhci, td);
	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td,
		hc32_to_cpu(uhci, td->link));
	out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
		((status >> 27) & 3),
		(status & TD_CTRL_SPD) ?      "SPD " : "",
@@ -63,7 +65,7 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
		(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
		status & 0x7ff);

	token = td_token(td);
	token = td_token(uhci, td);
	switch (uhci_packetid(token)) {
		case USB_PID_SETUP:
			spid = "SETUP";
@@ -86,12 +88,13 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
		(token >> 8) & 127,
		(token & 0xff),
		spid);
	out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
	out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer));

	return out - buf;
}

static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp,
			char *buf, int len, int space)
{
	char *out = buf;
	struct uhci_td *td;
@@ -130,9 +133,10 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
		if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
				(++i <= 10 || debug > 2)) {
			out += sprintf(out, "%*s%d: ", space + 2, "", i);
			out += uhci_show_td(td, out, len - (out - buf), 0);
			out += uhci_show_td(uhci, td, out,
					len - (out - buf), 0);
		} else {
			if (td_status(td) & TD_CTRL_ACTIVE)
			if (td_status(uhci, td) & TD_CTRL_ACTIVE)
				++nactive;
			else
				++ninactive;
@@ -151,7 +155,7 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
{
	char *out = buf;
	int i, nurbs;
	__le32 element = qh_element(qh);
	__hc32 element = qh_element(qh);
	char *qtype;

	/* Try to make sure there's enough memory */
@@ -168,7 +172,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci,

	out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
			space, "", qh, qtype,
			le32_to_cpu(qh->link), le32_to_cpu(element));
			hc32_to_cpu(uhci, qh->link),
			hc32_to_cpu(uhci, element));
	if (qh->type == USB_ENDPOINT_XFER_ISOC)
		out += sprintf(out, "%*s    period %d phase %d load %d us, "
				"frame %x desc [%p]\n",
@@ -178,22 +183,22 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
		out += sprintf(out, "%*s    period %d phase %d load %d us\n",
				space, "", qh->period, qh->phase, qh->load);

	if (element & UHCI_PTR_QH)
	if (element & UHCI_PTR_QH(uhci))
		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");

	if (element & UHCI_PTR_DEPTH)
	if (element & UHCI_PTR_DEPTH(uhci))
		out += sprintf(out, "%*s  Depth traverse\n", space, "");

	if (element & cpu_to_le32(8))
	if (element & cpu_to_hc32(uhci, 8))
		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");

	if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
	if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci))))
		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");

	if (list_empty(&qh->queue)) {
		out += sprintf(out, "%*s  queue is empty\n", space, "");
		if (qh == uhci->skel_async_qh)
			out += uhci_show_td(uhci->term_td, out,
			out += uhci_show_td(uhci, uhci->term_td, out,
					len - (out - buf), 0);
	} else {
		struct urb_priv *urbp = list_entry(qh->queue.next,
@@ -201,13 +206,13 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
		struct uhci_td *td = list_entry(urbp->td_list.next,
				struct uhci_td, list);

		if (element != LINK_TO_TD(td))
		if (element != LINK_TO_TD(uhci, td))
			out += sprintf(out, "%*s Element != First TD\n",
					space, "");
		i = nurbs = 0;
		list_for_each_entry(urbp, &qh->queue, node) {
			if (++i <= 10)
				out += uhci_show_urbp(urbp, out,
				out += uhci_show_urbp(uhci, urbp, out,
						len - (out - buf), space + 2);
			else
				++nurbs;
@@ -219,7 +224,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci,

	if (qh->dummy_td) {
		out += sprintf(out, "%*s  Dummy TD\n", space, "");
		out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0);
		out += uhci_show_td(uhci, qh->dummy_td, out,
				len - (out - buf), 0);
	}

	return out - buf;
@@ -346,8 +352,8 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
	struct uhci_td *td;
	struct list_head *tmp, *head;
	int nframes, nerrs;
	__le32 link;
	__le32 fsbr_link;
	__hc32 link;
	__hc32 fsbr_link;

	static const char * const qh_names[] = {
		"unlink", "iso", "int128", "int64", "int32", "int16",
@@ -375,7 +381,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
	nframes = 10;
	nerrs = 0;
	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
		__le32 qh_dma;
		__hc32 qh_dma;

		j = 0;
		td = uhci->frame_cpu[i];
@@ -385,7 +391,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)

		if (nframes > 0) {
			out += sprintf(out, "- Frame %d -> (%08x)\n",
					i, le32_to_cpu(link));
					i, hc32_to_cpu(uhci, link));
			j = 1;
		}

@@ -394,7 +400,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
		do {
			td = list_entry(tmp, struct uhci_td, fl_list);
			tmp = tmp->next;
			if (link != LINK_TO_TD(td)) {
			if (link != LINK_TO_TD(uhci, td)) {
				if (nframes > 0)
					out += sprintf(out, "    link does "
						"not match list entry!\n");
@@ -402,7 +408,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
					++nerrs;
			}
			if (nframes > 0)
				out += uhci_show_td(td, out,
				out += uhci_show_td(uhci, td, out,
						len - (out - buf), 4);
			link = td->link;
		} while (tmp != head);
@@ -414,11 +420,12 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
				if (!j) {
					out += sprintf(out,
						"- Frame %d -> (%08x)\n",
						i, le32_to_cpu(link));
						i, hc32_to_cpu(uhci, link));
					j = 1;
				}
				out += sprintf(out, "   link does not match "
					"QH (%08x)!\n", le32_to_cpu(qh_dma));
					"QH (%08x)!\n",
					hc32_to_cpu(uhci, qh_dma));
			} else
				++nerrs;
		}
@@ -439,11 +446,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)

		/* Last QH is the Terminating QH, it's different */
		if (i == SKEL_TERM) {
			if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
			if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td))
				out += sprintf(out, "    skel_term_qh element is not set to term_td!\n");
			link = fsbr_link;
			if (!link)
				link = LINK_TO_QH(uhci->skel_term_qh);
				link = LINK_TO_QH(uhci, uhci->skel_term_qh);
			goto check_qh_link;
		}

@@ -457,20 +464,20 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
				out += uhci_show_qh(uhci, qh, out,
						len - (out - buf), 4);
			if (!fsbr_link && qh->skel >= SKEL_FSBR)
				fsbr_link = LINK_TO_QH(qh);
				fsbr_link = LINK_TO_QH(uhci, qh);
		}
		if ((cnt -= 10) > 0)
			out += sprintf(out, "    Skipped %d QHs\n", cnt);

		link = UHCI_PTR_TERM;
		link = UHCI_PTR_TERM(uhci);
		if (i <= SKEL_ISO)
			;
		else if (i < SKEL_ASYNC)
			link = LINK_TO_QH(uhci->skel_async_qh);
			link = LINK_TO_QH(uhci, uhci->skel_async_qh);
		else if (!uhci->fsbr_is_on)
			;
		else
			link = LINK_TO_QH(uhci->skel_term_qh);
			link = LINK_TO_QH(uhci, uhci->skel_term_qh);
check_qh_link:
		if (qh->link != link)
			out += sprintf(out, "    last QH not linked to next skeleton!\n");
+8 −8
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
/*
 * Calculate the link pointer DMA value for the first Skeleton QH in a frame.
 */
static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
{
	int skelnum;

@@ -114,7 +114,7 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
	skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
	if (skelnum <= 1)
		skelnum = 9;
	return LINK_TO_QH(uhci->skelqh[skelnum]);
	return LINK_TO_QH(uhci, uhci->skelqh[skelnum]);
}

#include "uhci-debug.c"
@@ -630,16 +630,16 @@ static int uhci_start(struct usb_hcd *hcd)
	 * 8 Interrupt queues; link all higher int queues to int1 = async
	 */
	for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
		uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
	uhci->skel_async_qh->link = UHCI_PTR_TERM;
	uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
		uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh);
	uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci);
	uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);

	/* This dummy TD is to work around a bug in Intel PIIX controllers */
	uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
	uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) |
			(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
	uhci->term_td->link = UHCI_PTR_TERM;
	uhci->term_td->link = UHCI_PTR_TERM(uhci);
	uhci->skel_async_qh->element = uhci->skel_term_qh->element =
			LINK_TO_TD(uhci->term_td);
		LINK_TO_TD(uhci, uhci->term_td);

	/*
	 * Fill the frame list: make all entries point to the proper
+74 −16
Original line number Diff line number Diff line
@@ -78,11 +78,11 @@
#define   USBPORT1EN		0x01
#define   USBPORT2EN		0x02

#define UHCI_PTR_BITS		cpu_to_le32(0x000F)
#define UHCI_PTR_TERM		cpu_to_le32(0x0001)
#define UHCI_PTR_QH		cpu_to_le32(0x0002)
#define UHCI_PTR_DEPTH		cpu_to_le32(0x0004)
#define UHCI_PTR_BREADTH	cpu_to_le32(0x0000)
#define UHCI_PTR_BITS(uhci)	cpu_to_hc32((uhci), 0x000F)
#define UHCI_PTR_TERM(uhci)	cpu_to_hc32((uhci), 0x0001)
#define UHCI_PTR_QH(uhci)	cpu_to_hc32((uhci), 0x0002)
#define UHCI_PTR_DEPTH(uhci)	cpu_to_hc32((uhci), 0x0004)
#define UHCI_PTR_BREADTH(uhci)	cpu_to_hc32((uhci), 0x0000)

#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
@@ -98,6 +98,22 @@
#define QH_WAIT_TIMEOUT		msecs_to_jiffies(200)


/*
 * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
 * __leXX (normally) or __beXX (given UHCI_BIG_ENDIAN_DESC), depending on
 * the host controller implementation.
 *
 * To facilitate the strongest possible byte-order checking from "sparse"
 * and so on, we use __leXX unless that's not practical.
 */
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC
typedef __u32 __bitwise __hc32;
typedef __u16 __bitwise __hc16;
#else
#define __hc32	__le32
#define __hc16	__le16
#endif

/*
 *	Queue Headers
 */
@@ -130,8 +146,8 @@

struct uhci_qh {
	/* Hardware fields */
	__le32 link;			/* Next QH in the schedule */
	__le32 element;			/* Queue element (TD) pointer */
	__hc32 link;			/* Next QH in the schedule */
	__hc32 element;			/* Queue element (TD) pointer */

	/* Software fields */
	dma_addr_t dma_handle;
@@ -170,7 +186,8 @@ struct uhci_qh {
 */
#define qh_element(qh)		ACCESS_ONCE((qh)->element)

#define LINK_TO_QH(qh)		(UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
#define LINK_TO_QH(uhci, qh)	(UHCI_PTR_QH((uhci)) | \
				cpu_to_hc32((uhci), (qh)->dma_handle))


/*
@@ -207,7 +224,7 @@ struct uhci_qh {
/*
 * for TD <info>: (a.k.a. Token)
 */
#define td_token(td)		le32_to_cpu((td)->token)
#define td_token(uhci, td)	hc32_to_cpu((uhci), (td)->token)
#define TD_TOKEN_DEVADDR_SHIFT	8
#define TD_TOKEN_TOGGLE_SHIFT	19
#define TD_TOKEN_TOGGLE		(1 << 19)
@@ -240,10 +257,10 @@ struct uhci_qh {
 */
struct uhci_td {
	/* Hardware fields */
	__le32 link;
	__le32 status;
	__le32 token;
	__le32 buffer;
	__hc32 link;
	__hc32 status;
	__hc32 token;
	__hc32 buffer;

	/* Software fields */
	dma_addr_t dma_handle;
@@ -258,9 +275,10 @@ struct uhci_td {
 * We need a special accessor for the control/status word because it is
 * subject to asynchronous updates by the controller.
 */
#define td_status(td)		le32_to_cpu(ACCESS_ONCE((td)->status))
#define td_status(uhci, td)		hc32_to_cpu((uhci), \
						ACCESS_ONCE((td)->status))

#define LINK_TO_TD(td)		(cpu_to_le32((td)->dma_handle))
#define LINK_TO_TD(uhci, td)		(cpu_to_hc32((uhci), (td)->dma_handle))


/*
@@ -383,7 +401,7 @@ struct uhci_hcd {
	spinlock_t lock;

	dma_addr_t frame_dma_handle;	/* Hardware frame list */
	__le32 *frame;
	__hc32 *frame;
	void **frame_cpu;		/* CPU's frame list */

	enum uhci_rh_state rh_state;
@@ -412,6 +430,7 @@ struct uhci_hcd {
	unsigned int oc_low:1;			/* OverCurrent bit active low */
	unsigned int wait_for_hp:1;		/* Wait for HP port reset */
	unsigned int big_endian_mmio:1;		/* Big endian registers */
	unsigned int big_endian_desc:1;		/* Big endian descriptors */

	/* Support for port suspend/resume/reset */
	unsigned long port_c_suspend;		/* Bit-arrays of ports */
@@ -603,4 +622,43 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
}
#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */

/*
 * The GRLIB GRUSBHC controller can use big endian format for its descriptors.
 *
 * UHCI controllers accessed through PCI work normally (little-endian
 * everywhere), so we don't bother supporting a BE-only mode.
 */
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC
#define uhci_big_endian_desc(u)		((u)->big_endian_desc)

/* cpu to uhci */
static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x)
{
	return uhci_big_endian_desc(uhci)
		? (__force __hc32)cpu_to_be32(x)
		: (__force __hc32)cpu_to_le32(x);
}

/* uhci to cpu */
static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x)
{
	return uhci_big_endian_desc(uhci)
		? be32_to_cpu((__force __be32)x)
		: le32_to_cpu((__force __le32)x);
}

#else
/* cpu to uhci */
static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x)
{
	return cpu_to_le32(x);
}

/* uhci to cpu */
static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x)
{
	return le32_to_cpu(x);
}
#endif

#endif
+67 −64

File changed.

Preview size limit exceeded, changes collapsed.