Commit 65603789 authored by Paul Mundt's avatar Paul Mundt

Merge branch 'sh/r8a66597-udc'

parents e638fab9 f8f8c079
......@@ -16,6 +16,8 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/usb/r8a66597.h>
#include <video/sh_mobile_lcdc.h>
#include <asm/clock.h>
#include <asm/machvec.h>
......@@ -175,6 +177,35 @@ static struct platform_device kfr2r09_sh_lcdc_device = {
},
};
static struct r8a66597_platdata kfr2r09_usb0_gadget_data = {
.on_chip = 1,
};
static struct resource kfr2r09_usb0_gadget_resources[] = {
[0] = {
.start = 0x04d80000,
.end = 0x04d80123,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 65,
.end = 65,
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
},
};
static struct platform_device kfr2r09_usb0_gadget_device = {
.name = "r8a66597_udc",
.id = 0,
.dev = {
.dma_mask = NULL, /* not use dma */
.coherent_dma_mask = 0xffffffff,
.platform_data = &kfr2r09_usb0_gadget_data,
},
.num_resources = ARRAY_SIZE(kfr2r09_usb0_gadget_resources),
.resource = kfr2r09_usb0_gadget_resources,
};
static struct platform_device *kfr2r09_devices[] __initdata = {
&kfr2r09_nor_flash_device,
&kfr2r09_nand_flash_device,
......@@ -186,6 +217,74 @@ static struct platform_device *kfr2r09_devices[] __initdata = {
#define BSC_CS0WCR 0xfec10024
#define BSC_CS4BCR 0xfec10010
#define BSC_CS4WCR 0xfec10030
#define PORT_MSELCRB 0xa4050182
static int kfr2r09_usb0_gadget_i2c_setup(void)
{
struct i2c_adapter *a;
struct i2c_msg msg;
unsigned char buf[2];
int ret;
a = i2c_get_adapter(0);
if (!a)
return -ENODEV;
/* set bit 1 (the second bit) of chip at 0x09, register 0x13 */
buf[0] = 0x13;
msg.addr = 0x09;
msg.buf = buf;
msg.len = 1;
msg.flags = 0;
ret = i2c_transfer(a, &msg, 1);
if (ret != 1)
return -ENODEV;
buf[0] = 0;
msg.addr = 0x09;
msg.buf = buf;
msg.len = 1;
msg.flags = I2C_M_RD;
ret = i2c_transfer(a, &msg, 1);
if (ret != 1)
return -ENODEV;
buf[1] = buf[0] | (1 << 1);
buf[0] = 0x13;
msg.addr = 0x09;
msg.buf = buf;
msg.len = 2;
msg.flags = 0;
ret = i2c_transfer(a, &msg, 1);
if (ret != 1)
return -ENODEV;
return 0;
}
static int kfr2r09_usb0_gadget_setup(void)
{
int plugged_in;
gpio_request(GPIO_PTN4, NULL); /* USB_DET */
gpio_direction_input(GPIO_PTN4);
plugged_in = gpio_get_value(GPIO_PTN4);
if (!plugged_in)
return -ENODEV; /* no cable plugged in */
if (kfr2r09_usb0_gadget_i2c_setup() != 0)
return -ENODEV; /* unable to configure using i2c */
ctrl_outw((ctrl_inw(PORT_MSELCRB) & ~0xc000) | 0x8000, PORT_MSELCRB);
gpio_request(GPIO_FN_PDSTATUS, NULL); /* R-standby disables USB clock */
gpio_request(GPIO_PTV6, NULL); /* USBCLK_ON */
gpio_direction_output(GPIO_PTV6, 1); /* USBCLK_ON = H */
msleep(20); /* wait 20ms to let the clock settle */
clk_enable(clk_get(NULL, "usb0"));
ctrl_outw(0x0600, 0xa40501d4);
return 0;
}
static int __init kfr2r09_devices_setup(void)
{
......@@ -245,6 +344,10 @@ static int __init kfr2r09_devices_setup(void)
gpio_request(GPIO_PTU0, NULL); /* LEDSTDBY/ */
gpio_direction_output(GPIO_PTU0, 1);
/* setup USB function */
if (kfr2r09_usb0_gadget_setup() == 0)
platform_device_register(&kfr2r09_usb0_gadget_device);
return platform_add_devices(kfr2r09_devices,
ARRAY_SIZE(kfr2r09_devices));
}
......
......@@ -341,6 +341,35 @@ static struct platform_device sh7724_usb0_host_device = {
.resource = sh7724_usb0_host_resources,
};
static struct r8a66597_platdata sh7724_usb1_gadget_data = {
.on_chip = 1,
};
static struct resource sh7724_usb1_gadget_resources[] = {
[0] = {
.start = 0xa4d90000,
.end = 0xa4d90123,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 66,
.end = 66,
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
},
};
static struct platform_device sh7724_usb1_gadget_device = {
.name = "r8a66597_udc",
.id = 1, /* USB1 */
.dev = {
.dma_mask = NULL, /* not use dma */
.coherent_dma_mask = 0xffffffff,
.platform_data = &sh7724_usb1_gadget_data,
},
.num_resources = ARRAY_SIZE(sh7724_usb1_gadget_resources),
.resource = sh7724_usb1_gadget_resources,
};
static struct platform_device *ms7724se_devices[] __initdata = {
&heartbeat_device,
&smc91x_eth_device,
......@@ -351,6 +380,7 @@ static struct platform_device *ms7724se_devices[] __initdata = {
&keysc_device,
&sh_eth_device,
&sh7724_usb0_host_device,
&sh7724_usb1_gadget_device,
};
#define EEPROM_OP 0xBA206000
......@@ -459,6 +489,9 @@ static int __init devices_setup(void)
/* enable USB0 port */
ctrl_outw(0x0600, 0xa40501d4);
/* enable USB1 port */
ctrl_outw(0x0600, 0xa4050192);
/* enable IRQ 0,1,2 */
gpio_request(GPIO_FN_INTC_IRQ0, NULL);
gpio_request(GPIO_FN_INTC_IRQ1, NULL);
......
......@@ -251,6 +251,24 @@ config USB_PXA25X_SMALL
default y if USB_ETH
default y if USB_G_SERIAL
config USB_GADGET_R8A66597
boolean "Renesas R8A66597 USB Peripheral Controller"
select USB_GADGET_DUALSPEED
help
R8A66597 is a discrete USB host and peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
It has nine configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "r8a66597_udc" and force all
gadget drivers to also be dynamically linked.
config USB_R8A66597
tristate
depends on USB_GADGET_R8A66597
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_PXA27X
boolean "PXA 27x"
depends on ARCH_PXA && (PXA27x || PXA3xx)
......
......@@ -23,6 +23,7 @@ ifeq ($(CONFIG_ARCH_MXC),y)
fsl_usb2_udc-objs += fsl_mx3_udc.o
endif
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
......
......@@ -173,6 +173,12 @@
// CONFIG_USB_GADGET_AU1X00
// ...
#ifdef CONFIG_USB_GADGET_R8A66597
#define gadget_is_r8a66597(g) !strcmp("r8a66597_udc", (g)->name)
#else
#define gadget_is_r8a66597(g) 0
#endif
/**
* usb_gadget_controller_number - support bcdDevice id convention
......@@ -239,6 +245,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x23;
else if (gadget_is_langwell(gadget))
return 0x24;
else if (gadget_is_r8a66597(gadget))
return 0x25;
return -ENOENT;
}
......
This diff is collapsed.
/*
* R8A66597 UDC
*
* Copyright (C) 2007-2009 Renesas Solutions Corp.
*
* Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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.
*
* 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
*
*/
#ifndef __R8A66597_H__
#define __R8A66597_H__
#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
#endif
#include <linux/usb/r8a66597.h>
#define R8A66597_MAX_SAMPLING 10
#define R8A66597_MAX_NUM_PIPE 8
#define R8A66597_MAX_NUM_BULK 3
#define R8A66597_MAX_NUM_ISOC 2
#define R8A66597_MAX_NUM_INT 2
#define R8A66597_BASE_PIPENUM_BULK 3
#define R8A66597_BASE_PIPENUM_ISOC 1
#define R8A66597_BASE_PIPENUM_INT 6
#define R8A66597_BASE_BUFNUM 6
#define R8A66597_MAX_BUFNUM 0x4F
#define is_bulk_pipe(pipenum) \
((pipenum >= R8A66597_BASE_PIPENUM_BULK) && \
(pipenum < (R8A66597_BASE_PIPENUM_BULK + R8A66597_MAX_NUM_BULK)))
#define is_interrupt_pipe(pipenum) \
((pipenum >= R8A66597_BASE_PIPENUM_INT) && \
(pipenum < (R8A66597_BASE_PIPENUM_INT + R8A66597_MAX_NUM_INT)))
#define is_isoc_pipe(pipenum) \
((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \
(pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC)))
struct r8a66597_pipe_info {
u16 pipe;
u16 epnum;
u16 maxpacket;
u16 type;
u16 interval;
u16 dir_in;
};
struct r8a66597_request {
struct usb_request req;
struct list_head queue;
};
struct r8a66597_ep {
struct usb_ep ep;
struct r8a66597 *r8a66597;
struct list_head queue;
unsigned busy:1;
unsigned internal_ccpl:1; /* use only control */
/* this member can able to after r8a66597_enable */
unsigned use_dma:1;
u16 pipenum;
u16 type;
const struct usb_endpoint_descriptor *desc;
/* register address */
unsigned char fifoaddr;
unsigned char fifosel;
unsigned char fifoctr;
unsigned char fifotrn;
unsigned char pipectr;
};
struct r8a66597 {
spinlock_t lock;
unsigned long reg;
#ifdef CONFIG_HAVE_CLK
struct clk *clk;
#endif
struct r8a66597_platdata *pdata;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE];
struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE];
struct r8a66597_ep *epaddr2ep[16];
struct timer_list timer;
struct usb_request *ep0_req; /* for internal request */
u16 ep0_data; /* for internal request */
u16 old_vbus;
u16 scount;
u16 old_dvsq;
/* pipe config */
unsigned char bulk;
unsigned char interrupt;
unsigned char isochronous;
unsigned char num_dma;
unsigned irq_sense_low:1;
};
#define gadget_to_r8a66597(_gadget) \
container_of(_gadget, struct r8a66597, gadget)
#define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget)
static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
{
return inw(r8a66597->reg + offset);
}
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
unsigned long offset, u16 *buf,
int len)
{
if (r8a66597->pdata->on_chip) {
unsigned long fifoaddr = r8a66597->reg + offset;
unsigned long count;
union {
unsigned long dword;
unsigned char byte[4];
} data;
unsigned char *pb;
int i;
count = len / 4;
insl(fifoaddr, buf, count);
if (len & 0x00000003) {
data.dword = inl(fifoaddr);
pb = (unsigned char *)buf + count * 4;
for (i = 0; i < (len & 0x00000003); i++)
pb[i] = data.byte[i];
}
} else {
len = (len + 1) / 2;
insw(r8a66597->reg + offset, buf, len);
}
}
static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
unsigned long offset)
{
outw(val, r8a66597->reg + offset);
}
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
unsigned long offset, u16 *buf,
int len)
{
unsigned long fifoaddr = r8a66597->reg + offset;
if (r8a66597->pdata->on_chip) {
unsigned long count;
unsigned char *pb;
int i;
count = len / 4;
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 {
int odd = len & 0x0001;
len = len / 2;
outsw(fifoaddr, buf, len);
if (unlikely(odd)) {
buf = &buf[len];
outb((unsigned char)*buf, fifoaddr);
}
}
}
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
u16 val, u16 pat, unsigned long offset)
{
u16 tmp;
tmp = r8a66597_read(r8a66597, offset);
tmp = tmp & (~pat);
tmp = tmp | val;
r8a66597_write(r8a66597, tmp, offset);
}
static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
{
u16 clock = 0;
switch (pdata->xtal) {
case R8A66597_PLATDATA_XTAL_12MHZ:
clock = XTAL12;
break;
case R8A66597_PLATDATA_XTAL_24MHZ:
clock = XTAL24;
break;
case R8A66597_PLATDATA_XTAL_48MHZ:
clock = XTAL48;
break;
default:
printk(KERN_ERR "r8a66597: platdata clock is wrong.\n");
break;
}
return clock;
}
#define r8a66597_bclr(r8a66597, val, offset) \
r8a66597_mdfy(r8a66597, 0, val, offset)
#define r8a66597_bset(r8a66597, val, offset) \
r8a66597_mdfy(r8a66597, val, 0, offset)
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
#define enable_irq_ready(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, BRDYENB)
#define disable_irq_ready(r8a66597, pipenum) \
disable_pipe_irq(r8a66597, pipenum, BRDYENB)
#define enable_irq_empty(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, BEMPENB)
#define disable_irq_empty(r8a66597, pipenum) \
disable_pipe_irq(r8a66597, pipenum, BEMPENB)
#define enable_irq_nrdy(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, NRDYENB)
#define disable_irq_nrdy(r8a66597, pipenum) \
disable_pipe_irq(r8a66597, pipenum, NRDYENB)
#endif /* __R8A66597_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment