mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
usb: r8a66597-udc unaligned fifo fix
Rework the r8a66597-udc fifo code to avoid unaligned accesses. Without this patch unaligned exceptions will degrade the USB performance. The exceptions come from the fact that the usb fifo data buffers may be misaligned. This patch updates the fifo access code to only use insl()/outsl() and insw()/outsw() in the case of properly aligned data buffers. The fallback case is that inl()/inw() are used for misaligned buffer reads together with outb() that is used for misaligned buffer writes. Signed-off-by: Magnus Damm <damm@opensource.se> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
parent
9c472c4dd8
commit
59c82d12aa
1 changed files with 60 additions and 41 deletions
|
@ -131,31 +131,48 @@ static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
|
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
|
||||||
unsigned long offset, u16 *buf,
|
unsigned long offset,
|
||||||
|
unsigned char *buf,
|
||||||
int len)
|
int len)
|
||||||
{
|
{
|
||||||
|
unsigned long fifoaddr = r8a66597->reg + offset;
|
||||||
|
unsigned int data;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (r8a66597->pdata->on_chip) {
|
if (r8a66597->pdata->on_chip) {
|
||||||
unsigned long fifoaddr = r8a66597->reg + offset;
|
/* 32-bit accesses for on_chip controllers */
|
||||||
unsigned long count;
|
|
||||||
union {
|
|
||||||
unsigned long dword;
|
|
||||||
unsigned char byte[4];
|
|
||||||
} data;
|
|
||||||
unsigned char *pb;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
count = len / 4;
|
/* aligned buf case */
|
||||||
insl(fifoaddr, buf, count);
|
if (len >= 4 && !((unsigned long)buf & 0x03)) {
|
||||||
|
insl(fifoaddr, buf, len / 4);
|
||||||
|
buf += len & ~0x03;
|
||||||
|
len &= 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
if (len & 0x00000003) {
|
/* unaligned buf case */
|
||||||
data.dword = inl(fifoaddr);
|
for (i = 0; i < len; i++) {
|
||||||
pb = (unsigned char *)buf + count * 4;
|
if (!(i & 0x03))
|
||||||
for (i = 0; i < (len & 0x00000003); i++)
|
data = inl(fifoaddr);
|
||||||
pb[i] = data.byte[i];
|
|
||||||
|
buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
len = (len + 1) / 2;
|
/* 16-bit accesses for external controllers */
|
||||||
insw(r8a66597->reg + offset, buf, len);
|
|
||||||
|
/* aligned buf case */
|
||||||
|
if (len >= 2 && !((unsigned long)buf & 0x01)) {
|
||||||
|
insw(fifoaddr, buf, len / 2);
|
||||||
|
buf += len & ~0x01;
|
||||||
|
len &= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unaligned buf case */
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (!(i & 0x01))
|
||||||
|
data = inw(fifoaddr);
|
||||||
|
|
||||||
|
buf[i] = (data >> ((i & 0x01) * 8)) & 0xff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,38 +183,40 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
|
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
|
||||||
unsigned long offset, u16 *buf,
|
unsigned long offset,
|
||||||
|
unsigned char *buf,
|
||||||
int len)
|
int len)
|
||||||
{
|
{
|
||||||
unsigned long fifoaddr = r8a66597->reg + offset;
|
unsigned long fifoaddr = r8a66597->reg + offset;
|
||||||
|
int adj = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (r8a66597->pdata->on_chip) {
|
if (r8a66597->pdata->on_chip) {
|
||||||
unsigned long count;
|
/* 32-bit access only if buf is 32-bit aligned */
|
||||||
unsigned char *pb;
|
if (len >= 4 && !((unsigned long)buf & 0x03)) {
|
||||||
int i;
|
outsl(fifoaddr, buf, len / 4);
|
||||||
|
buf += len & ~0x03;
|
||||||
count = len / 4;
|
len &= 0x03;
|
||||||
outsl(fifoaddr, buf, count);
|
|
||||||
|
|
||||||
if (len & 0x00000003) {
|
|
||||||
pb = (unsigned char *)buf + count * 4;
|
|
||||||
for (i = 0; i < (len & 0x00000003); i++) {
|
|
||||||
if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)
|
|
||||||
outb(pb[i], fifoaddr + i);
|
|
||||||
else
|
|
||||||
outb(pb[i], fifoaddr + 3 - i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int odd = len & 0x0001;
|
/* 16-bit access only if buf is 16-bit aligned */
|
||||||
|
if (len >= 2 && !((unsigned long)buf & 0x01)) {
|
||||||
len = len / 2;
|
outsw(fifoaddr, buf, len / 2);
|
||||||
outsw(fifoaddr, buf, len);
|
buf += len & ~0x01;
|
||||||
if (unlikely(odd)) {
|
len &= 0x01;
|
||||||
buf = &buf[len];
|
|
||||||
outb((unsigned char)*buf, fifoaddr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* adjust fifo address in the little endian case */
|
||||||
|
if (!(r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)) {
|
||||||
|
if (r8a66597->pdata->on_chip)
|
||||||
|
adj = 0x03; /* 32-bit wide */
|
||||||
|
else
|
||||||
|
adj = 0x01; /* 16-bit wide */
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
outb(buf[i], fifoaddr + adj - (i & adj));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
|
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
|
||||||
|
|
Loading…
Reference in a new issue