summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-09-18 09:43:09 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-18 09:43:09 -0700
commit515b696b282f856c3ad1679ccd658120faa387d0 (patch)
treed9d7c1185c396617f128ca23463062308d11393b /drivers
parentfa877c71e2136bd682b45022c96d5e073ced9f58 (diff)
parent064a16dc41be879d12bd5de5d2f9d38d890e0ee7 (diff)
downloadlinux-515b696b282f856c3ad1679ccd658120faa387d0.tar.bz2
Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6: (262 commits) sh: mach-ecovec24: Add user debug switch support sh: Kill off unused se_skipped in alignment trap notification code. sh: Wire up HAVE_SYSCALL_TRACEPOINTS. video: sh_mobile_lcdcfb: use both register sets for display panning video: sh_mobile_lcdcfb: implement display panning sh: Fix up sh7705 flush_dcache_page() build. sh: kfr2r09: document the PLL/FLL <-> RF relationship. sh: mach-ecovec24: need asm/clock.h. sh: mach-ecovec24: deassert usb irq on boot. sh: Add KEYSC support for EcoVec24 sh: add kycr2_delay for sh_keysc sh: cpufreq: Include CPU id in info messages. sh: multi-evt support for SH-X3 proto CPU. sh: clkfwk: remove bogus set_bus_parent() from SH7709. sh: Fix the indication point of the liquid crystal of AP-325RXA(AP3300) sh: Add EcoVec24 romImage defconfig sh: USB disable process is needed if romImage boot for EcoVec24 sh: EcoVec24: add HIZA setting for LED sh: EcoVec24: write MAC address in boot sh: Add romImage support for EcoVec24 ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c39
-rw-r--r--drivers/input/keyboard/sh_keysc.c3
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c41
-rw-r--r--drivers/rtc/rtc-ds1302.c69
-rw-r--r--drivers/rtc/rtc-sh.c97
-rw-r--r--drivers/serial/sh-sci.c8
-rw-r--r--drivers/serial/sh-sci.h17
-rw-r--r--drivers/sh/intc.c71
-rw-r--r--drivers/uio/uio_pdrv_genirq.c54
-rw-r--r--drivers/usb/gadget/Kconfig28
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/gadget_chips.h8
-rw-r--r--drivers/usb/gadget/m66592-udc.c286
-rw-r--r--drivers/usb/gadget/m66592-udc.h90
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c1689
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h256
-rw-r--r--drivers/usb/host/Kconfig7
-rw-r--r--drivers/usb/host/r8a66597-hcd.c210
-rw-r--r--drivers/usb/host/r8a66597.h440
-rw-r--r--drivers/video/Kconfig2
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c292
21 files changed, 2776 insertions, 932 deletions
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 820487d0d5c7..86a9d4e81472 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/io.h>
@@ -165,7 +166,8 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
u_int32_t denom;
u_int32_t tmp;
- /* Make sure the clock is enabled */
+ /* Wake up device and enable clock */
+ pm_runtime_get_sync(pd->dev);
clk_enable(pd->clk);
/* Get clock rate after clock is enabled */
@@ -213,8 +215,9 @@ static void deactivate_ch(struct sh_mobile_i2c_data *pd)
/* Disable channel */
iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
- /* Disable clock */
+ /* Disable clock and mark device as idle */
clk_disable(pd->clk);
+ pm_runtime_put_sync(pd->dev);
}
static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
@@ -572,6 +575,19 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_irq;
}
+ /* Enable Runtime PM for this device.
+ *
+ * Also tell the Runtime PM core to ignore children
+ * for this device since it is valid for us to suspend
+ * this I2C master driver even though the slave devices
+ * on the I2C bus may not be suspended.
+ *
+ * The state of the I2C hardware bus is unaffected by
+ * the Runtime PM state.
+ */
+ pm_suspend_ignore_children(&dev->dev, true);
+ pm_runtime_enable(&dev->dev);
+
/* setup the private data */
adap = &pd->adap;
i2c_set_adapdata(adap, pd);
@@ -614,14 +630,33 @@ static int sh_mobile_i2c_remove(struct platform_device *dev)
iounmap(pd->reg);
sh_mobile_i2c_hook_irqs(dev, 0);
clk_put(pd->clk);
+ pm_runtime_disable(&dev->dev);
kfree(pd);
return 0;
}
+static int sh_mobile_i2c_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
+ .runtime_suspend = sh_mobile_i2c_runtime_nop,
+ .runtime_resume = sh_mobile_i2c_runtime_nop,
+};
+
static struct platform_driver sh_mobile_i2c_driver = {
.driver = {
.name = "i2c-sh_mobile",
.owner = THIS_MODULE,
+ .pm = &sh_mobile_i2c_dev_pm_ops,
},
.probe = sh_mobile_i2c_probe,
.remove = sh_mobile_i2c_remove,
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 0714bf2c28fc..887af79b7bff 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -80,6 +80,9 @@ static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8),
priv->iomem_base + KYCR2_OFFS);
+ if (pdata->kycr2_delay)
+ udelay(pdata->kycr2_delay);
+
keys ^= ~0;
keys &= (1 << (sh_keysc_mode[pdata->mode].keyin *
sh_keysc_mode[pdata->mode].keyout)) - 1;
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index e86878deea71..61c47b824083 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -30,7 +30,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
-#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
@@ -86,7 +86,6 @@ struct sh_mobile_ceu_dev {
unsigned int irq;
void __iomem *base;
- struct clk *clk;
unsigned long video_limit;
/* lock used to protect videobuf */
@@ -361,7 +360,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
if (ret)
goto err;
- clk_enable(pcdev->clk);
+ pm_runtime_get_sync(ici->dev);
ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
while (ceu_read(pcdev, CSTSR) & 1)
@@ -395,7 +394,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
}
spin_unlock_irqrestore(&pcdev->lock, flags);
- clk_disable(pcdev->clk);
+ pm_runtime_put_sync(ici->dev);
icd->ops->release(icd);
@@ -798,7 +797,6 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
struct sh_mobile_ceu_dev *pcdev;
struct resource *res;
void __iomem *base;
- char clk_name[8];
unsigned int irq;
int err = 0;
@@ -862,13 +860,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
goto exit_release_mem;
}
- snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id);
- pcdev->clk = clk_get(&pdev->dev, clk_name);
- if (IS_ERR(pcdev->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
- err = PTR_ERR(pcdev->clk);
- goto exit_free_irq;
- }
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
pcdev->ici.priv = pcdev;
pcdev->ici.dev = &pdev->dev;
@@ -878,12 +872,10 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
err = soc_camera_host_register(&pcdev->ici);
if (err)
- goto exit_free_clk;
+ goto exit_free_irq;
return 0;
-exit_free_clk:
- clk_put(pcdev->clk);
exit_free_irq:
free_irq(pcdev->irq, pcdev);
exit_release_mem:
@@ -904,7 +896,6 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
struct sh_mobile_ceu_dev, ici);
soc_camera_host_unregister(soc_host);
- clk_put(pcdev->clk);
free_irq(pcdev->irq, pcdev);
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
dma_release_declared_memory(&pdev->dev);
@@ -913,9 +904,27 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
return 0;
}
+static int sh_mobile_ceu_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
+ .runtime_suspend = sh_mobile_ceu_runtime_nop,
+ .runtime_resume = sh_mobile_ceu_runtime_nop,
+};
+
static struct platform_driver sh_mobile_ceu_driver = {
.driver = {
.name = "sh_mobile_ceu",
+ .pm = &sh_mobile_ceu_dev_pm_ops,
},
.probe = sh_mobile_ceu_probe,
.remove = sh_mobile_ceu_remove,
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 184556620778..d490628b64da 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -1,26 +1,25 @@
/*
* Dallas DS1302 RTC Support
*
- * Copyright (C) 2002 David McCullough
- * Copyright (C) 2003 - 2007 Paul Mundt
+ * Copyright (C) 2002 David McCullough
+ * Copyright (C) 2003 - 2007 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
- * License version 2. See the file "COPYING" in the main directory of
+ * License version 2. See the file "COPYING" in the main directory of
* this archive for more details.
*/
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
-#include <linux/time.h>
#include <linux/rtc.h>
-#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/bcd.h>
#include <asm/rtc.h>
#define DRV_NAME "rtc-ds1302"
-#define DRV_VERSION "0.1.0"
+#define DRV_VERSION "0.1.1"
#define RTC_CMD_READ 0x81 /* Read command */
#define RTC_CMD_WRITE 0x80 /* Write command */
@@ -47,11 +46,6 @@
#error "Add support for your platform"
#endif
-struct ds1302_rtc {
- struct rtc_device *rtc_dev;
- spinlock_t lock;
-};
-
static void ds1302_sendbits(unsigned int val)
{
int i;
@@ -103,10 +97,6 @@ static void ds1302_writebyte(unsigned int addr, unsigned int val)
static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct ds1302_rtc *rtc = dev_get_drvdata(dev);
-
- spin_lock_irq(&rtc->lock);
-
tm->tm_sec = bcd2bin(ds1302_readbyte(RTC_ADDR_SEC));
tm->tm_min = bcd2bin(ds1302_readbyte(RTC_ADDR_MIN));
tm->tm_hour = bcd2bin(ds1302_readbyte(RTC_ADDR_HOUR));
@@ -118,26 +108,17 @@ static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (tm->tm_year < 70)
tm->tm_year += 100;
- spin_unlock_irq(&rtc->lock);
-
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
- if (rtc_valid_tm(tm) < 0)
- dev_err(dev, "invalid date\n");
-
- return 0;
+ return rtc_valid_tm(tm);
}
static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct ds1302_rtc *rtc = dev_get_drvdata(dev);
-
- spin_lock_irq(&rtc->lock);
-
/* Stop RTC */
ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80);
@@ -152,8 +133,6 @@ static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm)
/* Start RTC */
ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80);
- spin_unlock_irq(&rtc->lock);
-
return 0;
}
@@ -170,9 +149,7 @@ static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd,
if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int)))
return -EFAULT;
- spin_lock_irq(&rtc->lock);
ds1302_writebyte(RTC_ADDR_TCR, (0xa0 | tcs_val * 0xf));
- spin_unlock_irq(&rtc->lock);
return 0;
}
#endif
@@ -187,10 +164,9 @@ static struct rtc_class_ops ds1302_rtc_ops = {
.ioctl = ds1302_rtc_ioctl,
};
-static int __devinit ds1302_rtc_probe(struct platform_device *pdev)
+static int __init ds1302_rtc_probe(struct platform_device *pdev)
{
- struct ds1302_rtc *rtc;
- int ret;
+ struct rtc_device *rtc;
/* Reset */
set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
@@ -200,37 +176,23 @@ static int __devinit ds1302_rtc_probe(struct platform_device *pdev)
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
return -ENODEV;
- rtc = kzalloc(sizeof(struct ds1302_rtc), GFP_KERNEL);
- if (unlikely(!rtc))
- return -ENOMEM;
-
- spin_lock_init(&rtc->lock);
- rtc->rtc_dev = rtc_device_register("ds1302", &pdev->dev,
+ rtc = rtc_device_register("ds1302", &pdev->dev,
&ds1302_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
- ret = PTR_ERR(rtc->rtc_dev);
- goto out;
- }
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
platform_set_drvdata(pdev, rtc);
return 0;
-out:
- kfree(rtc);
- return ret;
}
static int __devexit ds1302_rtc_remove(struct platform_device *pdev)
{
- struct ds1302_rtc *rtc = platform_get_drvdata(pdev);
-
- if (likely(rtc->rtc_dev))
- rtc_device_unregister(rtc->rtc_dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ rtc_device_unregister(rtc);
platform_set_drvdata(pdev, NULL);
- kfree(rtc);
-
return 0;
}
@@ -239,13 +201,12 @@ static struct platform_driver ds1302_platform_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
- .probe = ds1302_rtc_probe,
- .remove = __devexit_p(ds1302_rtc_remove),
+ .remove = __exit_p(ds1302_rtc_remove),
};
static int __init ds1302_rtc_init(void)
{
- return platform_driver_register(&ds1302_platform_driver);
+ return platform_driver_probe(&ds1302_platform_driver, ds1302_rtc_probe);
}
static void __exit ds1302_rtc_exit(void)
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index d7310adb7152..e6ed5404bca0 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -29,7 +29,7 @@
#include <asm/rtc.h>
#define DRV_NAME "sh-rtc"
-#define DRV_VERSION "0.2.2"
+#define DRV_VERSION "0.2.3"
#define RTC_REG(r) ((r) * rtc_reg_size)
@@ -215,7 +215,7 @@ static irqreturn_t sh_rtc_shared(int irq, void *dev_id)
return IRQ_RETVAL(ret);
}
-static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
+static int sh_rtc_irq_set_state(struct device *dev, int enable)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
@@ -225,17 +225,22 @@ static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
tmp = readb(rtc->regbase + RCR2);
if (enable) {
+ rtc->periodic_freq |= PF_KOU;
tmp &= ~RCR2_PEF; /* Clear PES bit */
tmp |= (rtc->periodic_freq & ~PF_HP); /* Set PES2-0 */
- } else
+ } else {
+ rtc->periodic_freq &= ~PF_KOU;
tmp &= ~(RCR2_PESMASK | RCR2_PEF);
+ }
writeb(tmp, rtc->regbase + RCR2);
spin_unlock_irq(&rtc->lock);
+
+ return 0;
}
-static inline int sh_rtc_setfreq(struct device *dev, unsigned int freq)
+static int sh_rtc_irq_set_freq(struct device *dev, int freq)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
int tmp, ret = 0;
@@ -278,10 +283,8 @@ static inline int sh_rtc_setfreq(struct device *dev, unsigned int freq)
ret = -ENOTSUPP;
}
- if (ret == 0) {
+ if (ret == 0)
rtc->periodic_freq |= tmp;
- rtc->rtc_dev->irq_freq = freq;
- }
spin_unlock_irq(&rtc->lock);
return ret;
@@ -346,10 +349,6 @@ static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
unsigned int ret = 0;
switch (cmd) {
- case RTC_PIE_OFF:
- case RTC_PIE_ON:
- sh_rtc_setpie(dev, cmd == RTC_PIE_ON);
- break;
case RTC_AIE_OFF:
case RTC_AIE_ON:
sh_rtc_setaie(dev, cmd == RTC_AIE_ON);
@@ -362,13 +361,6 @@ static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
rtc->periodic_freq |= PF_OXS;
sh_rtc_setcie(dev, 1);
break;
- case RTC_IRQP_READ:
- ret = put_user(rtc->rtc_dev->irq_freq,
- (unsigned long __user *)arg);
- break;
- case RTC_IRQP_SET:
- ret = sh_rtc_setfreq(dev, arg);
- break;
default:
ret = -ENOIOCTLCMD;
}
@@ -602,28 +594,6 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
return 0;
}
-static int sh_rtc_irq_set_state(struct device *dev, int enabled)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_rtc *rtc = platform_get_drvdata(pdev);
-
- if (enabled) {
- rtc->periodic_freq |= PF_KOU;
- return sh_rtc_ioctl(dev, RTC_PIE_ON, 0);
- } else {
- rtc->periodic_freq &= ~PF_KOU;
- return sh_rtc_ioctl(dev, RTC_PIE_OFF, 0);
- }
-}
-
-static int sh_rtc_irq_set_freq(struct device *dev, int freq)
-{
- if (!is_power_of_2(freq))
- return -EINVAL;
-
- return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq);
-}
-
static struct rtc_class_ops sh_rtc_ops = {
.ioctl = sh_rtc_ioctl,
.read_time = sh_rtc_read_time,
@@ -635,7 +605,7 @@ static struct rtc_class_ops sh_rtc_ops = {
.proc = sh_rtc_proc,
};
-static int __devinit sh_rtc_probe(struct platform_device *pdev)
+static int __init sh_rtc_probe(struct platform_device *pdev)
{
struct sh_rtc *rtc;
struct resource *res;
@@ -702,13 +672,6 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
clk_enable(rtc->clk);
- rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
- &sh_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev)) {
- ret = PTR_ERR(rtc->rtc_dev);
- goto err_unmap;
- }
-
rtc->capabilities = RTC_DEF_CAPABILITIES;
if (pdev->dev.platform_data) {
struct sh_rtc_platform_info *pinfo = pdev->dev.platform_data;
@@ -720,10 +683,6 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
rtc->capabilities |= pinfo->capabilities;
}
- rtc->rtc_dev->max_user_freq = 256;
-
- platform_set_drvdata(pdev, rtc);
-
if (rtc->carry_irq <= 0) {
/* register shared periodic/carry/alarm irq */
ret = request_irq(rtc->periodic_irq, sh_rtc_shared,
@@ -767,13 +726,26 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
}
}
+ platform_set_drvdata(pdev, rtc);
+
/* everything disabled by default */
- rtc->periodic_freq = 0;
- rtc->rtc_dev->irq_freq = 0;
- sh_rtc_setpie(&pdev->dev, 0);
+ sh_rtc_irq_set_freq(&pdev->dev, 0);
+ sh_rtc_irq_set_state(&pdev->dev, 0);
sh_rtc_setaie(&pdev->dev, 0);
sh_rtc_setcie(&pdev->dev, 0);
+ rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
+ &sh_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ ret = PTR_ERR(rtc->rtc_dev);
+ free_irq(rtc->periodic_irq, rtc);
+ free_irq(rtc->carry_irq, rtc);
+ free_irq(rtc->alarm_irq, rtc);
+ goto err_unmap;
+ }
+
+ rtc->rtc_dev->max_user_freq = 256;
+
/* reset rtc to epoch 0 if time is invalid */
if (rtc_read_time(rtc->rtc_dev, &r) < 0) {
rtc_time_to_tm(0, &r);
@@ -795,14 +767,13 @@ err_badres:
return ret;
}
-static int __devexit sh_rtc_remove(struct platform_device *pdev)
+static int __exit sh_rtc_remove(struct platform_device *pdev)
{
struct sh_rtc *rtc = platform_get_drvdata(pdev);
- if (likely(rtc->rtc_dev))
- rtc_device_unregister(rtc->rtc_dev);
+ rtc_device_unregister(rtc->rtc_dev);
+ sh_rtc_irq_set_state(&pdev->dev, 0);
- sh_rtc_setpie(&pdev->dev, 0);
sh_rtc_setaie(&pdev->dev, 0);
sh_rtc_setcie(&pdev->dev, 0);
@@ -813,9 +784,8 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev)
free_irq(rtc->alarm_irq, rtc);
}
- release_resource(rtc->res);
-
iounmap(rtc->regbase);
+ release_resource(rtc->res);
clk_disable(rtc->clk);
clk_put(rtc->clk);
@@ -867,13 +837,12 @@ static struct platform_driver sh_rtc_platform_driver = {
.owner = THIS_MODULE,
.pm = &sh_rtc_dev_pm_ops,
},
- .probe = sh_rtc_probe,
- .remove = __devexit_p(sh_rtc_remove),
+ .remove = __exit_p(sh_rtc_remove),
};
static int __init sh_rtc_init(void)
{
- return platform_driver_register(&sh_rtc_platform_driver);
+ return platform_driver_probe(&sh_rtc_platform_driver, sh_rtc_probe);
}
static void __exit sh_rtc_exit(void)
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 8e2feb563347..32dc2fc50e6b 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -272,7 +272,8 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
__raw_writew(data, PSCR);
}
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
+#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785) || \
defined(CONFIG_CPU_SUBTYPE_SH7786) || \
@@ -662,10 +663,11 @@ static irqreturn_t sci_rx_interrupt(int irq, void *port)
static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
+ unsigned long flags;
- spin_lock_irq(&port->lock);
+ spin_lock_irqsave(&port->lock, flags);
sci_transmit_chars(port);
- spin_unlock_irq(&port->lock);
+ spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED;
}
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 38072c15b845..3e2fcf93b42e 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -112,6 +112,13 @@
#elif defined(CONFIG_H8S2678)
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7757)
+# define SCSPTR0 0xfe4b0020
+# define SCSPTR1 0xfe4b0020
+# define SCSPTR2 0xfe4b0020
+# define SCIF_ORER 0x0001
+# define SCSCR_INIT(port) 0x38
+# define SCIF_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
# define SCSPTR1 0xffe08024 /* 16 bit SCIF */
@@ -562,6 +569,16 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
return 1;
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7757)
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->mapbase == 0xfe4b0000)
+ return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0;
+ if (port->mapbase == 0xfe4c0000)
+ return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0;
+ if (port->mapbase == 0xfe4d0000)
+ return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0;
+}
#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
static inline int sci_rxd_in(struct uart_port *port)
{
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 3dd231a643b5..559b5fe9dc0f 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -77,7 +77,7 @@ static unsigned long ack_handle[NR_IRQS];
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
{
struct irq_chip *chip = get_irq_chip(irq);
- return (void *)((char *)chip - offsetof(struct intc_desc_int, chip));
+ return container_of(chip, struct intc_desc_int, chip);
}
static inline unsigned int set_field(unsigned int value,
@@ -95,16 +95,19 @@ static inline unsigned int set_field(unsigned int value,
static void write_8(unsigned long addr, unsigned long h, unsigned long data)
{
__raw_writeb(set_field(0, data, h), addr);
+ (void)__raw_readb(addr); /* Defeat write posting */
}
static void write_16(unsigned long addr, unsigned long h, unsigned long data)
{
__raw_writew(set_field(0, data, h), addr);
+ (void)__raw_readw(addr); /* Defeat write posting */
}
static void write_32(unsigned long addr, unsigned long h, unsigned long data)
{
__raw_writel(set_field(0, data, h), addr);
+ (void)__raw_readl(addr); /* Defeat write posting */
}
static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
@@ -112,6 +115,7 @@ static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
unsigned long flags;
local_irq_save(flags);
__raw_writeb(set_field(__raw_readb(addr), data, h), addr);
+ (void)__raw_readb(addr); /* Defeat write posting */
local_irq_restore(flags);
}
@@ -120,6 +124,7 @@ static void modify_16(unsigned long addr, unsigned long h, unsigned long data)
unsigned long flags;
local_irq_save(flags);
__raw_writew(set_field(__raw_readw(addr), data, h), addr);
+ (void)__raw_readw(addr); /* Defeat write posting */
local_irq_restore(flags);
}
@@ -128,6 +133,7 @@ static void modify_32(unsigned long addr, unsigned long h, unsigned long data)
unsigned long flags;
local_irq_save(flags);
__raw_writel(set_field(__raw_readl(addr), data, h), addr);
+ (void)__raw_readl(addr); /* Defeat write posting */
local_irq_restore(flags);
}
@@ -657,16 +663,9 @@ static unsigned int __init save_reg(struct intc_desc_int *d,
return 0;
}
-static unsigned char *intc_evt2irq_table;
-
-unsigned int intc_evt2irq(unsigned int vector)
+static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
{
- unsigned int irq = evt2irq(vector);
-
- if (intc_evt2irq_table && intc_evt2irq_table[irq])
- irq = intc_evt2irq_table[irq];
-
- return irq;
+ generic_handle_irq((unsigned int)get_irq_data(irq));
}
void __init register_intc_controller(struct intc_desc *desc)
@@ -739,50 +738,48 @@ void __init register_intc_controller(struct intc_desc *desc)
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
- /* keep the first vector only if same enum is used multiple times */
+ /* register the vectors one by one */
for (i = 0; i < desc->nr_vectors; i++) {
struct intc_vect *vect = desc->vectors + i;
- int first_irq = evt2irq(vect->vect);
+ unsigned int irq = evt2irq(vect->vect);
+ struct irq_desc *irq_desc;
if (!vect->enum_id)
continue;
+ irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
+ if (unlikely(!irq_desc)) {
+ pr_info("can't get irq_desc for %d\n", irq);
+ continue;
+ }
+
+ intc_register_irq(desc, d, vect->enum_id, irq);
+
for (k = i + 1; k < desc->nr_vectors; k++) {
struct intc_vect *vect2 = desc->vectors + k;
+ unsigned int irq2 = evt2irq(vect2->vect);
if (vect->enum_id != vect2->enum_id)
continue;
- vect2->enum_id = 0;
-
- if (!intc_evt2irq_table)
- intc_evt2irq_table = kzalloc(NR_IRQS, GFP_NOWAIT);
-
- if (!intc_evt2irq_table) {
- pr_warning("intc: cannot allocate evt2irq!\n");
+ /*
+ * In the case of multi-evt handling and sparse
+ * IRQ support, each vector still needs to have
+ * its own backing irq_desc.
+ */
+ irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id());
+ if (unlikely(!irq_desc)) {
+ pr_info("can't get irq_desc for %d\n", irq2);
continue;
}
- intc_evt2irq_table[evt2irq(vect2->vect)] = first_irq;
- }
- }
-
- /* register the vectors one by one */
- for (i = 0; i < desc->nr_vectors; i++) {
- struct intc_vect *vect = desc->vectors + i;
- unsigned int irq = evt2irq(vect->vect);
- struct irq_desc *irq_desc;
-
- if (!vect->enum_id)
- continue;
+ vect2->enum_id = 0;
- irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
- if (unlikely(!irq_desc)) {
- printk(KERN_INFO "can not get irq_desc for %d\n", irq);
- continue;
+ /* redirect this interrupts to the first one */
+ set_irq_chip_and_handler_name(irq2, &d->chip,
+ intc_redirect_irq, "redirect");
+ set_irq_data(irq2, (void *)irq);
}
-
- intc_register_irq(desc, d, vect->enum_id, irq);
}
}
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 3f06818cf9fa..02347c57357d 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -20,6 +20,7 @@
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/stringify.h>
+#include <linux/pm_runtime.h>
#define DRIVER_NAME "uio_pdrv_genirq"
@@ -27,8 +28,27 @@ struct uio_pdrv_genirq_platdata {
struct uio_info *uioinfo;
spinlock_t lock;
unsigned long flags;
+ struct platform_device *pdev;
};
+static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode)
+{
+ struct uio_pdrv_genirq_platdata *priv = info->priv;
+
+ /* Wait until the Runtime PM code has woken up the device */
+ pm_runtime_get_sync(&priv->pdev->dev);
+ return 0;
+}
+
+static int uio_pdrv_genirq_release(struct uio_info *info, struct inode *inode)
+{
+ struct uio_pdrv_genirq_platdata *priv = info->priv;
+
+ /* Tell the Runtime PM code that the device has become idle */
+ pm_runtime_put_sync(&priv->pdev->dev);
+ return 0;
+}
+
static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
{
struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
@@ -97,6 +117,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
priv->uioinfo = uioinfo;
spin_lock_init(&priv->lock);
priv->flags = 0; /* interrupt is enabled to begin with */
+ priv->pdev = pdev;
uiomem = &uioinfo->mem[0];
@@ -136,8 +157,17 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
uioinfo->irq_flags |= IRQF_DISABLED;
uioinfo->handler = uio_pdrv_genirq_handler;
uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol;
+ uioinfo->open = uio_pdrv_genirq_open;
+ uioinfo->release = uio_pdrv_genirq_release;
uioinfo->priv = priv;
+ /* Enable Runtime PM for this device:
+ * The device starts in suspended state to allow the hardware to be
+ * turned off by default. The Runtime PM bus code should power on the
+ * hardware and enable clocks at open().
+ */
+ pm_runtime_enable(&pdev->dev);
+
ret = uio_register_device(&pdev->dev, priv->uioinfo);
if (ret) {
dev_err(&pdev->dev, "unable to register uio device\n");
@@ -157,16 +187,40 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev)
struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);
uio_unregister_device(priv->uioinfo);
+ pm_runtime_disable(&pdev->dev);
kfree(priv);
return 0;
}
+static int uio_pdrv_genirq_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
+ * are used at open() and release() time. This allows the
+ * Runtime PM code to turn off power to the device while the
+ * device is unused, ie before open() and after release().
+ *
+ * This Runtime PM callback does not need to save or restore
+ * any registers since user space is responsbile for hardware
+ * register reinitialization after open().
+ */
+ return 0;
+}
+
+static struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
+ .runtime_suspend = uio_pdrv_genirq_runtime_nop,
+ .runtime_resume = uio_pdrv_genirq_runtime_nop,
+};
+
static struct platform_driver uio_pdrv_genirq = {
.probe = uio_pdrv_genirq_probe,
.remove = uio_pdrv_genirq_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &uio_pdrv_genirq_dev_pm_ops,
},
};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 7f8e83a954ac..9f986b417c5b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -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)
@@ -360,16 +378,6 @@ config USB_M66592
default USB_GADGET
select USB_GADGET_SELECTED
-config SUPERH_BUILT_IN_M66592
- boolean "Enable SuperH built-in USB like the M66592"
- depends on USB_GADGET_M66592 && CPU_SUBTYPE_SH7722
- help
- SH7722 has USB like the M66592.
-
- The transfer rate is very slow when use "Ethernet Gadget".
- However, this problem is improved if change a value of
- NET_IP_ALIGN to 4.
-
#
# Controllers available only in discrete form (and all PCI controllers)
#
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index e6017e6bf6da..9d7b87c52e9f 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -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
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 8e0e9a0b7364..f2d270b202f2 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -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;
}
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index 43dcf9e1af6b..a8c8543d1b08 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -25,44 +25,18 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
-
+#include <linux/err.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "m66592-udc.h"
-
MODULE_DESCRIPTION("M66592 USB gadget driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_ALIAS("platform:m66592_udc");
-#define DRIVER_VERSION "18 Oct 2007"
-
-/* module parameters */
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
-static unsigned short endian = M66592_LITTLE;
-module_param(endian, ushort, 0644);
-MODULE_PARM_DESC(endian, "data endian: big=0, little=0 (default=0)");
-#else
-static unsigned short clock = M66592_XTAL24;
-module_param(clock, ushort, 0644);
-MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 "
- "(default=16384)");
-
-static unsigned short vif = M66592_LDRV;
-module_param(vif, ushort, 0644);
-MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0 (default=32768)");
-
-static unsigned short endian;
-module_param(endian, ushort, 0644);
-MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)");
-
-static unsigned short irq_sense = M66592_INTL;
-module_param(irq_sense, ushort, 0644);
-MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0 "
- "(default=2)");
-#endif
+#define DRIVER_VERSION "21 July 2009"
static const char udc_name[] = "m66592_udc";
static const char *m66592_ep_name[] = {
@@ -244,6 +218,7 @@ static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum)
static inline void pipe_change(struct m66592 *m66592, u16 pipenum)
{
struct m66592_ep *ep = m66592->pipenum2ep[pipenum];
+ unsigned short mbw;
if (ep->use_dma)
return;
@@ -252,7 +227,12 @@ static inline void pipe_change(struct m66592 *m66592, u16 pipenum)
ndelay(450);
- m66592_bset(m66592, M66592_MBW, ep->fifosel);
+ if (m66592->pdata->on_chip)
+ mbw = M66592_MBW_32;
+ else
+ mbw = M66592_MBW_16;
+
+ m66592_bset(m66592, mbw, ep->fifosel);
}
static int pipe_buffer_setting(struct m66592 *m66592,
@@ -276,24 +256,27 @@ static int pipe_buffer_setting(struct m66592 *m66592,
buf_bsize = 0;
break;
case M66592_BULK:
- bufnum = m66592->bi_bufnum +
- (info->pipe - M66592_BASE_PIPENUM_BULK) * 16;
- m66592->bi_bufnum += 16;
+ /* isochronous pipes may be used as bulk pipes */
+ if (info->pipe > M66592_BASE_PIPENUM_BULK)
+ bufnum = info->pipe - M66592_BASE_PIPENUM_BULK;
+ else
+ bufnum = info->pipe - M66592_BASE_PIPENUM_ISOC;
+
+ bufnum = M66592_BASE_BUFNUM + (bufnum * 16);
buf_bsize = 7;
pipecfg |= M66592_DBLB;
if (!info->dir_in)
pipecfg |= M66592_SHTNAK;
break;
case M66592_ISO:
- bufnum = m66592->bi_bufnum +
+ bufnum = M66592_BASE_BUFNUM +
(info->pipe - M66592_BASE_PIPENUM_ISOC) * 16;
- m66592->bi_bufnum += 16;
buf_bsize = 7;
break;
}
- if (m66592->bi_bufnum > M66592_MAX_BUFNUM) {
- pr_err("m66592 pipe memory is insufficient(%d)\n",
- m66592->bi_bufnum);
+
+ if (buf_bsize && ((bufnum + 16) >= M66592_MAX_BUFNUM)) {
+ pr_err("m66592 pipe memory is insufficient\n");
return -ENOMEM;
}
@@ -313,17 +296,6 @@ static void pipe_buffer_release(struct m66592 *m66592,
if (info->pipe == 0)
return;
- switch (info->type) {
- case M66592_BULK:
- if (is_bulk_pipe(info->pipe))
- m66592->bi_bufnum -= 16;
- break;
- case M66592_ISO:
- if (is_isoc_pipe(info->pipe))
- m66592->bi_bufnum -= 16;
- break;
- }
-
if (is_bulk_pipe(info->pipe)) {
m66592->bulk--;
} else if (is_interrupt_pipe(info->pipe))
@@ -340,6 +312,7 @@ static void pipe_buffer_release(struct m66592 *m66592,
static void pipe_initialize(struct m66592_ep *ep)
{
struct m66592 *m66592 = ep->m66592;
+ unsigned short mbw;
m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel);
@@ -351,7 +324,12 @@ static void pipe_initialize(struct m66592_ep *ep)
ndelay(450);
- m66592_bset(m66592, M66592_MBW, ep->fifosel);
+ if (m66592->pdata->on_chip)
+ mbw = M66592_MBW_32;
+ else
+ mbw = M66592_MBW_16;
+
+ m66592_bset(m66592, mbw, ep->fifosel);
}
}
@@ -367,15 +345,13 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
ep->fifosel = M66592_D0FIFOSEL;
ep->fifoctr = M66592_D0FIFOCTR;
ep->fifotrn = M66592_D0FIFOTRN;
-#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
- } else if (m66592->num_dma == 1) {
+ } else if (!m66592->pdata->on_chip && m66592->num_dma == 1) {
m66592->num_dma++;
ep->use_dma = 1;
ep->fifoaddr = M66592_D1FIFO;
ep->fifosel = M66592_D1FIFOSEL;
ep->fifoctr = M66592_D1FIFOCTR;
ep->fifotrn = M66592_D1FIFOTRN;
-#endif
} else {
ep->use_dma = 0;
ep->fifoaddr = M66592_CFIFO;
@@ -620,76 +596,120 @@ static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
}
}
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
static void init_controller(struct m66592 *m66592)
{
- m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */
- m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
- m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
- m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+ unsigned int endian;
- /* This is a workaound for SH7722 2nd cut */
- m66592_bset(m66592, 0x8000, M66592_DVSTCTR);
- m66592_bset(m66592, 0x1000, M66592_TESTMODE);
- m66592_bclr(m66592, 0x8000, M66592_DVSTCTR);
+ if (m66592->pdata->on_chip) {
+ if (m66592->pdata->endian)
+ endian = 0; /* big endian */
+ else
+ endian = M66592_LITTLE; /* little endian */
- m66592_bset(m66592, M66592_INTL, M66592_INTENB1);
+ m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */
+ m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
+ m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+ m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
- m66592_write(m66592, 0, M66592_CFBCFG);
- m66592_write(m66592, 0, M66592_D0FBCFG);
- m66592_bset(m66592, endian, M66592_CFBCFG);
- m66592_bset(m66592, endian, M66592_D0FBCFG);
-}
-#else /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
-static void init_controller(struct m66592 *m66592)
-{
- m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND),
- M66592_PINCFG);
- m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */
- m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG);
+ /* This is a workaound for SH7722 2nd cut */
+ m66592_bset(m66592, 0x8000, M66592_DVSTCTR);
+ m66592_bset(m66592, 0x1000, M66592_TESTMODE);
+ m66592_bclr(m66592, 0x8000, M66592_DVSTCTR);
- m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
- m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
- m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+ m66592_bset(m66592, M66592_INTL, M66592_INTENB1);
+
+ m66592_write(m66592, 0, M66592_CFBCFG);
+ m66592_write(m66592, 0, M66592_D0FBCFG);
+ m66592_bset(m66592, endian, M66592_CFBCFG);
+ m66592_bset(m66592, endian, M66592_D0FBCFG);
+ } else {
+ unsigned int clock, vif, irq_sense;
+
+ if (m66592->pdata->endian)
+ endian = M66592_BIGEND; /* big endian */
+ else
+ endian = 0; /* little endian */
+
+ if (m66592->pdata->vif)
+ vif = M66592_LDRV; /* 3.3v */
+ else
+ vif = 0; /* 1.5v */
+
+ switch (m66592->pdata->xtal) {
+ case M66592_PLATDATA_XTAL_12MHZ:
+ clock = M66592_XTAL12;
+ break;
+ case M66592_PLATDATA_XTAL_24MHZ:
+ clock = M66592_XTAL24;
+ break;
+ case M66592_PLATDATA_XTAL_48MHZ:
+ clock = M66592_XTAL48;
+ break;
+ default:
+ pr_warning("m66592-udc: xtal configuration error\n");
+ clock = 0;
+ }
+
+ switch (m66592->irq_trigger) {
+ case IRQF_TRIGGER_LOW:
+ irq_sense = M66592_INTL;
+ break;
+ case IRQF_TRIGGER_FALLING:
+ irq_sense = 0;
+ break;
+ default:
+ pr_warning("m66592-udc: irq trigger config error\n");
+ irq_sense = 0;
+ }
- m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+ m66592_bset(m66592,
+ (vif & M66592_LDRV) | (endian & M66592_BIGEND),
+ M66592_PINCFG);
+ m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */
+ m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL,
+ M66592_SYSCFG);
+ m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
+ m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+ m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+
+ m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
- msleep(3);
+ msleep(3);
- m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+ m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
- msleep(1);
+ msleep(1);
- m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+ m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
- m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
- m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
- M66592_DMA0CFG);
+ m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
+ m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
+ M66592_DMA0CFG);
+ }
}
-#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
static void disable_controller(struct m66592 *m66592)
{
-#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
- m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
- udelay(1);
- m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
- udelay(1);
- m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
- udelay(1);
- m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
-#endif
+ if (!m66592->pdata->on_chip) {
+ m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
+ udelay(1);
+ m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
+ udelay(1);
+ m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
+ udelay(1);
+ m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
+ }
}
static void m66592_start_xclock(struct m66592 *m66592)
{
-#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
u16 tmp;
- tmp = m66592_read(m66592, M66592_SYSCFG);
- if (!(tmp & M66592_XCKE))
- m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
-#endif
+ if (!m66592->pdata->on_chip) {
+ tmp = m66592_read(m66592, M66592_SYSCFG);
+ if (!(tmp & M66592_XCKE))
+ m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+ }
}
/*-------------------------------------------------------------------------*/
@@ -1177,8 +1197,7 @@ static irqreturn_t m66592_irq(int irq, void *_m66592)
intsts0 = m66592_read(m66592, M66592_INTSTS0);
intenb0 = m66592_read(m66592, M66592_INTENB0);
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
- if (!intsts0 && !intenb0) {
+ if (m66592->pdata->on_chip && !intsts0 && !intenb0) {
/*
* When USB clock stops, it cannot read register. Even if a
* clock stops, the interrupt occurs. So this driver turn on
@@ -1188,7 +1207,6 @@ static irqreturn_t m66592_irq(int irq, void *_m66592)
intsts0 = m66592_read(m66592, M66592_INTSTS0);
intenb0 = m66592_read(m66592, M66592_INTENB0);
}
-#endif
savepipe = m66592_read(m66592, M66592_CFIFOSEL);
@@ -1534,9 +1552,11 @@ static int __exit m66592_remove(struct platform_device *pdev)
iounmap(m66592->reg);
free_irq(platform_get_irq(pdev, 0), m66592);
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
- clk_disable(m66592->clk);
- clk_put(m66592->clk);
+#ifdef CONFIG_HAVE_CLK
+ if (m66592->pdata->on_chip) {
+ clk_disable(m66592->clk);
+ clk_put(m66592->clk);
+ }
#endif
kfree(m66592);
return 0;
@@ -1548,11 +1568,10 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
static int __init m66592_probe(struct platform_device *pdev)
{
- struct resource *res;
- int irq;
+ struct resource *res, *ires;
void __iomem *reg = NULL;
struct m66592 *m66592 = NULL;
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
char clk_name[8];
#endif
int ret = 0;
@@ -1565,10 +1584,11 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!ires) {
ret = -ENODEV;
- pr_err("platform_get_irq error.\n");
+ dev_err(&pdev->dev,
+ "platform_get_resource IORESOURCE_IRQ error.\n");
goto clean_up;
}
@@ -1579,6 +1599,12 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ ret = -ENODEV;
+ goto clean_up;
+ }
+
/* initialize ucd */
m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
if (m66592 == NULL) {
@@ -1586,6 +1612,9 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
+ m66592->pdata = pdev->dev.platform_data;
+ m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
+
spin_lock_init(&m66592->lock);
dev_set_drvdata(&pdev->dev, m66592);
@@ -1603,24 +1632,25 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592->timer.data = (unsigned long)m66592;
m66592->reg = reg;
- m66592->bi_bufnum = M66592_BASE_BUFNUM;
-
- ret = request_irq(irq, m66592_irq, IRQF_DISABLED | IRQF_SHARED,
+ ret = request_irq(ires->start, m66592_irq, IRQF_DISABLED | IRQF_SHARED,
udc_name, m66592);
if (ret < 0) {
pr_err("request_irq error (%d)\n", ret);
goto clean_up;
}
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
- snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
- m66592->clk = clk_get(&pdev->dev, clk_name);
- if (IS_ERR(m66592->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
- ret = PTR_ERR(m66592->clk);
- goto clean_up2;
+#ifdef CONFIG_HAVE_CLK
+ if (m66592->pdata->on_chip) {
+ snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
+ m66592->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(m66592->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
+ clk_name);
+ ret = PTR_ERR(m66592->clk);
+ goto clean_up2;
+ }
+ clk_enable(m66592->clk);
}
- clk_enable(m66592->clk);
#endif
INIT_LIST_HEAD(&m66592->gadget.ep_list);
m66592->gadget.ep0 = &m66592->ep[0].ep;
@@ -1662,12 +1692,14 @@ static int __init m66592_probe(struct platform_device *pdev)
return 0;
clean_up3:
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
- clk_disable(m66592->clk);
- clk_put(m66592->clk);
+#ifdef CONFIG_HAVE_CLK
+ if (m66592->pdata->on_chip) {
+ clk_disable(m66592->clk);
+ clk_put(m66592->clk);
+ }
clean_up2:
#endif
- free_irq(irq, m66592);
+ free_irq(ires->start, m66592);
clean_up:
if (m66592) {
if (m66592->ep0_req)
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
index 286ce07e7960..8b960deed680 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -23,10 +23,12 @@
#ifndef __M66592_UDC_H__
#define __M66592_UDC_H__
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
#endif
+#include <linux/usb/m66592.h>
+
#define M66592_SYSCFG 0x00
#define M66592_XTAL 0xC000 /* b15-14: Crystal selection */
#define M66592_XTAL48 0x8000 /* 48MHz */
@@ -76,11 +78,11 @@
#define M66592_P_TST_J 0x0001 /* PERI TEST J */
#define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
+/* built-in registers */
#define M66592_CFBCFG 0x0A
#define M66592_D0FBCFG 0x0C
#define M66592_LITTLE 0x0100 /* b8: Little endian mode */
-#else
+/* external chip case */
#define M66592_PINCFG 0x0A
#define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */
#define M66592_BIGEND 0x0100 /* b8: Big endian mode */
@@ -100,8 +102,8 @@
#define M66592_PKTM 0x0020 /* b5: Packet mode */
#define M66592_DENDE 0x0010 /* b4: Dend enable */
#define M66592_OBUS 0x0004 /* b2: OUTbus mode */
-#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
+/* common case */
#define M66592_CFIFO 0x10
#define M66592_D0FIFO 0x14
#define M66592_D1FIFO 0x18
@@ -113,13 +115,9 @@
#define M66592_REW 0x4000 /* b14: Buffer rewind */
#define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */
#define M66592_DREQE 0x1000 /* b12: DREQ output enable */
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
-#define M66592_MBW 0x0800 /* b11: Maximum bit width for FIFO */
-#else
-#define M66592_MBW 0x0400 /* b10: Maximum bit width for FIFO */
-#define M66592_MBW_8 0x0000 /* 8bit */
-#define M66592_MBW_16 0x0400 /* 16bit */
-#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
+#define M66592_MBW_8 0x0000 /* 8bit */
+#define M66592_MBW_16 0x0400 /* 16bit */
+#define M66592_MBW_32 0x0800 /* 32bit */
#define M66592_TRENB 0x0200 /* b9: Transaction counter enable */
#define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */
#define M66592_DEZPM 0x0080 /* b7: Zero-length packet mode */
@@ -480,9 +478,11 @@ struct m66592_ep {
struct m66592 {
spinlock_t lock;
void __iomem *reg;
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
struct clk *clk;
#endif
+ struct m66592_platdata *pdata;
+ unsigned long irq_trigger;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
@@ -506,7 +506,6 @@ struct m66592 {
int interrupt;
int isochronous;
int num_dma;
- int bi_bufnum; /* bulk and isochronous's bufnum */
};
#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
@@ -547,13 +546,13 @@ static inline void m66592_read_fifo(struct m66592 *m66592,
{
unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
- len = (len + 3) / 4;
- insl(fifoaddr, buf, len);
-#else
- len = (len + 1) / 2;
- insw(fifoaddr, buf, len);
-#endif
+ if (m66592->pdata->on_chip) {
+ len = (len + 3) / 4;
+ insl(fifoaddr, buf, len);
+ } else {
+ len = (len + 1) / 2;
+ insw(fifoaddr, buf, len);
+ }
}
static inline void m66592_write(struct m66592 *m66592, u16 val,
@@ -567,33 +566,34 @@ static inline void m66592_write_fifo(struct m66592 *m66592,
void *buf, unsigned long len)
{
unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
- unsigned long count;
- unsigned char *pb;
- int i;
-
- count = len / 4;
- outsl(fifoaddr, buf, count);
-
- if (len & 0x00000003) {
- pb = buf + count * 4;
- for (i = 0; i < (len & 0x00000003); i++) {
- if (m66592_read(m66592, M66592_CFBCFG)) /* little */
- outb(pb[i], fifoaddr + (3 - i));
- else
- outb(pb[i], fifoaddr + i);
+
+ if (m66592->pdata->on_chip) {
+ unsigned long count;
+ unsigned char *pb;
+ int i;
+
+ count = len / 4;
+ outsl(fifoaddr, buf, count);
+
+ if (len & 0x00000003) {
+ pb = buf + count * 4;
+ for (i = 0; i < (len & 0x00000003); i++) {
+ if (m66592_read(m66592, M66592_CFBCFG)) /* le */
+ outb(pb[i], fifoaddr + (3 - i));
+ else
+ outb(pb[i], fifoaddr + i);
+ }
+ }
+ } else {
+ unsigned long odd = len & 0x0001;
+
+ len = len / 2;
+ outsw(fifoaddr, buf, len);
+ if (odd) {
+ unsigned char *p = buf + len*2;
+ outb(*p, fifoaddr);
}
}
-#else
- unsigned long odd = len & 0x0001;
-
- len = len / 2;
- outsw(fifoaddr, buf, len);
- if (odd) {
- unsigned char *p = buf + len*2;
- outb(*p, fifoaddr);
- }
-#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
}
static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
new file mode 100644
index 000000000000..e220fb8091a3
--- /dev/null
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -0,0 +1,1689 @@
+/*
+ * R8A66597 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-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
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "r8a66597-udc.h"
+
+#define DRIVER_VERSION "2009-08-18"
+
+static const char udc_name[] = "r8a66597_udc";
+static const char *r8a66597_ep_name[] = {
+ "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7",
+ "ep8", "ep9",
+};
+
+static void disable_controller(struct r8a66597 *r8a66597);
+static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req);
+static void irq_packet_write(struct r8a66597_ep *ep,
+ struct r8a66597_request *req);
+static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags);
+
+static void transfer_complete(struct r8a66597_ep *ep,
+ struct r8a66597_request *req, int status);
+
+/*-------------------------------------------------------------------------*/
+static inline u16 get_usb_speed(struct r8a66597 *r8a66597)
+{
+ return r8a66597_read(r8a66597, DVSTCTR0) & RHST;
+}
+
+static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+ unsigned long reg)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE,
+ INTENB0);
+ r8a66597_bset(r8a66597, (1 << pipenum), reg);
+ r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+ unsigned long reg)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE,
+ INTENB0);
+ r8a66597_bclr(r8a66597, (1 << pipenum), reg);
+ r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void r8a66597_usb_connect(struct r8a66597 *r8a66597)
+{
+ r8a66597_bset(r8a66597, CTRE, INTENB0);
+ r8a66597_bset(r8a66597, BEMPE | BRDYE, INTENB0);
+
+ r8a66597_bset(r8a66597, DPRPU, SYSCFG0);
+}
+
+static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+ r8a66597_bclr(r8a66597, CTRE, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | BRDYE, INTENB0);
+ r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+
+ r8a66597->gadget.speed = USB_SPEED_UNKNOWN;
+ spin_unlock(&r8a66597->lock);
+ r8a66597->driver->disconnect(&r8a66597->gadget);
+ spin_lock(&r8a66597->lock);
+
+ disable_controller(r8a66597);
+ INIT_LIST_HEAD(&r8a66597->ep[0].queue);
+}
+
+static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 pid = 0;
+ unsigned long offset;
+
+ if (pipenum == 0)
+ pid = r8a66597_read(r8a66597, DCPCTR) & PID;
+ else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ offset = get_pipectr_addr(pipenum);
+ pid = r8a66597_read(r8a66597, offset) & PID;
+ } else
+ printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+
+ return pid;
+}
+
+static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum,
+ u16 pid)
+{
+ unsigned long offset;
+
+ if (pipenum == 0)
+ r8a66597_mdfy(r8a66597, pid, PID, DCPCTR);
+ else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ offset = get_pipectr_addr(pipenum);
+ r8a66597_mdfy(r8a66597, pid, PID, offset);
+ } else
+ printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+}
+
+static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ control_reg_set_pid(r8a66597, pipenum, PID_BUF);
+}
+
+static inline void pipe_stop(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ control_reg_set_pid(r8a66597, pipenum, PID_NAK);
+}
+
+static inline void pipe_stall(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ control_reg_set_pid(r8a66597, pipenum, PID_STALL);
+}
+
+static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 ret = 0;
+ unsigned long offset;
+
+ if (pipenum == 0)
+ ret = r8a66597_read(r8a66597, DCPCTR);
+ else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ offset = get_pipectr_addr(pipenum);
+ ret = r8a66597_read(r8a66597, offset);
+ } else
+ printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+
+ return ret;
+}
+
+static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ unsigned long offset;
+
+ pipe_stop(r8a66597, pipenum);
+
+ if (pipenum == 0)
+ r8a66597_bset(r8a66597, SQCLR, DCPCTR);
+ else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ offset = get_pipectr_addr(pipenum);
+ r8a66597_bset(r8a66597, SQCLR, offset);
+ } else
+ printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum);
+}
+
+static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 tmp;
+ int size;
+
+ if (pipenum == 0) {
+ tmp = r8a66597_read(r8a66597, DCPCFG);
+ if ((tmp & R8A66597_CNTMD) != 0)
+ size = 256;
+ else {
+ tmp = r8a66597_read(r8a66597, DCPMAXP);
+ size = tmp & MAXP;
+ }
+ } else {
+ r8a66597_write(r8a66597, pipenum, PIPESEL);
+ tmp = r8a66597_read(r8a66597, PIPECFG);
+ if ((tmp & R8A66597_CNTMD) != 0) {
+ tmp = r8a66597_read(r8a66597, PIPEBUF);
+ size = ((tmp >> 10) + 1) * 64;
+ } else {
+ tmp = r8a66597_read(r8a66597, PIPEMAXP);
+ size = tmp & MXPS;
+ }
+ }
+
+ return size;
+}
+
+static inline unsigned short mbw_value(struct r8a66597 *r8a66597)
+{
+ if (r8a66597->pdata->on_chip)
+ return MBW_32;
+ else
+ return MBW_16;
+}
+
+static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];
+
+ if (ep->use_dma)
+ return;
+
+ r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel);
+
+ ndelay(450);
+
+ r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel);
+}
+
+static int pipe_buffer_setting(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe_info *info)
+{
+ u16 bufnum = 0, buf_bsize = 0;
+ u16 pipecfg = 0;
+
+ if (info->pipe == 0)
+ return -EINVAL;
+
+ r8a66597_write(r8a66597, info->pipe, PIPESEL);
+
+ if (info->dir_in)
+ pipecfg |= R8A66597_DIR;
+ pipecfg |= info->type;
+ pipecfg |= info->epnum;
+ switch (info->type) {
+ case R8A66597_INT:
+ bufnum = 4 + (info->pipe - R8A66597_BASE_PIPENUM_INT);
+ buf_bsize = 0;
+ break;
+ case R8A66597_BULK:
+ /* isochronous pipes may be used as bulk pipes */
+ if (info->pipe > R8A66597_BASE_PIPENUM_BULK)
+ bufnum = info->pipe - R8A66597_BASE_PIPENUM_BULK;
+ else
+ bufnum = info->pipe - R8A66597_BASE_PIPENUM_ISOC;
+
+ bufnum = R8A66597_BASE_BUFNUM + (bufnum * 16);
+ buf_bsize = 7;
+ pipecfg |= R8A66597_DBLB;
+ if (!info->dir_in)
+ pipecfg |= R8A66597_SHTNAK;
+ break;
+ case R8A66597_ISO:
+ bufnum = R8A66597_BASE_BUFNUM +
+ (info->pipe - R8A66597_BASE_PIPENUM_ISOC) * 16;
+ buf_bsize = 7;
+ break;
+ }
+
+ if (buf_bsize && ((bufnum + 16) >= R8A66597_MAX_BUFNUM)) {
+ pr_err(KERN_ERR "r8a66597 pipe memory is insufficient\n");
+ return -ENOMEM;
+ }
+
+ r8a66597_write(r8a66597, pipecfg, PIPECFG);
+ r8a66597_write(r8a66597, (buf_bsize << 10) | (bufnum), PIPEBUF);
+ r8a66597_write(r8a66597, info->maxpacket, PIPEMAXP);
+ if (info->interval)
+ info->interval--;
+ r8a66597_write(r8a66597, info->interval, PIPEPERI);
+
+ return 0;
+}
+
+static void pipe_buffer_release(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe_info *info)
+{
+ if (info->pipe == 0)
+ return;
+
+ if (is_bulk_pipe(info->pipe))
+ r8a66597->bulk--;
+ else if (is_interrupt_pipe(info->pipe))
+ r8a66597->interrupt--;
+ else if (is_isoc_pipe(info->pipe)) {
+ r8a66597->isochronous--;
+ if (info->type == R8A66597_BULK)
+ r8a66597->bulk--;
+ } else
+ printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n",
+ info->pipe);
+}
+
+static void pipe_initialize(struct r8a66597_ep *ep)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+
+ r8a66597_mdfy(r8a66597, 0, CURPIPE, ep->fifosel);
+
+ r8a66597_write(r8a66597, ACLRM, ep->pipectr);
+ r8a66597_write(r8a66597, 0, ep->pipectr);
+ r8a66597_write(r8a66597, SQCLR, ep->pipectr);
+ if (ep->use_dma) {
+ r8a66597_mdfy(r8a66597, ep->pipenum, CURPIPE, ep->fifosel);
+
+ ndelay(450);
+
+ r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel);
+ }
+}
+
+static void r8a66597_ep_setting(struct r8a66597 *r8a66597,
+ struct r8a66597_ep *ep,
+ const struct usb_endpoint_descriptor *desc,
+ u16 pipenum, int dma)
+{
+ ep->use_dma = 0;
+ ep->fifoaddr = CFIFO;
+ ep->fifosel = CFIFOSEL;
+ ep->fifoctr = CFIFOCTR;
+ ep->fifotrn = 0;
+
+ ep->pipectr = get_pipectr_addr(pipenum);
+ ep->pipenum = pipenum;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ r8a66597->pipenum2ep[pipenum] = ep;
+ r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK]
+ = ep;
+ INIT_LIST_HEAD(&ep->queue);
+}
+
+static void r8a66597_ep_release(struct r8a66597_ep *ep)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+ u16 pipenum = ep->pipenum;
+
+ if (pipenum == 0)
+ return;
+
+ if (ep->use_dma)
+ r8a66597->num_dma--;
+ ep->pipenum = 0;
+ ep->busy = 0;
+ ep->use_dma = 0;
+}
+
+static int alloc_pipe_config(struct r8a66597_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+ struct r8a66597_pipe_info info;
+ int dma = 0;
+ unsigned char *counter;
+ int ret;
+
+ ep->desc = desc;
+
+ if (ep->pipenum) /* already allocated pipe */
+ return 0;
+
+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) {
+ if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
+ printk(KERN_ERR "bulk pipe is insufficient\n");
+ return -ENODEV;
+ } else {
+ info.pipe = R8A66597_BASE_PIPENUM_ISOC
+ + r8a66597->isochronous;
+ counter = &r8a66597->isochronous;
+ }
+ } else {
+ info.pipe = R8A66597_BASE_PIPENUM_BULK + r8a66597->bulk;
+ counter = &r8a66597->bulk;
+ }
+ info.type = R8A66597_BULK;
+ dma = 1;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) {
+ printk(KERN_ERR "interrupt pipe is insufficient\n");
+ return -ENODEV;
+ }
+ info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt;
+ info.type = R8A66597_INT;
+ counter = &r8a66597->interrupt;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
+ printk(KERN_ERR "isochronous pipe is insufficient\n");
+ return -ENODEV;
+ }
+ info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous;
+ info.type = R8A66597_ISO;
+ counter = &r8a66597->isochronous;
+ break;
+ default:
+ printk(KERN_ERR "unexpect xfer type\n");
+ return -EINVAL;
+ }
+ ep->type = info.type;
+
+ info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ info.interval = desc->bInterval;
+ if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ info.dir_in = 1;
+ else
+ info.dir_in = 0;
+
+ ret = pipe_buffer_setting(r8a66597, &info);
+ if (ret < 0) {
+ printk(KERN_ERR "pipe_buffer_setting fail\n");
+ return ret;
+ }
+
+ (*counter)++;
+ if ((counter == &r8a66597->isochronous) && info.type == R8A66597_BULK)
+ r8a66597->bulk++;
+
+ r8a66597_ep_setting(r8a66597, ep, desc, info.pipe, dma);
+ pipe_initialize(ep);
+
+ return 0;
+}
+
+static int free_pipe_config(struct r8a66597_ep *ep)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+ struct r8a66597_pipe_info info;
+
+ info.pipe = ep->pipenum;
+ info.type = ep->type;
+ pipe_buffer_release(r8a66597, &info);
+ r8a66597_ep_release(ep);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void pipe_irq_enable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ enable_irq_ready(r8a66597, pipenum);
+ enable_irq_nrdy(r8a66597, pipenum);
+}
+
+static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ disable_irq_ready(r8a66597, pipenum);
+ disable_irq_nrdy(r8a66597, pipenum);
+}
+
+/* if complete is true, gadget driver complete function is not call */
+static void control_end(struct r8a66597 *r8a66597, unsigned ccpl)
+{
+ r8a66597->ep[0].internal_ccpl = ccpl;
+ pipe_start(r8a66597, 0);
+ r8a66597_bset(r8a66597, CCPL, DCPCTR);
+}
+
+static void start_ep0_write(struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+
+ pipe_change(r8a66597, ep->pipenum);
+ r8a66597_mdfy(r8a66597, ISEL, (ISEL | CURPIPE), CFIFOSEL);
+ r8a66597_write(r8a66597, BCLR, ep->fifoctr);
+ if (req->req.length == 0) {
+ r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
+ pipe_start(r8a66597, 0);
+ transfer_complete(ep, req, 0);
+ } else {
+ r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
+ irq_ep0_write(ep, req);
+ }
+}
+
+static void start_packet_write(struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+ u16 tmp;
+
+ pipe_change(r8a66597, ep->pipenum);
+ disable_irq_empty(r8a66597, ep->pipenum);
+ pipe_start(r8a66597, ep->pipenum);
+
+ tmp = r8a66597_read(r8a66597, ep->fifoctr);
+ if (unlikely((tmp & FRDY) == 0))
+ pipe_irq_enable(r8a66597, ep->pipenum);
+ else
+ irq_packet_write(ep, req);
+}
+
+static void start_packet_read(struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+ u16 pipenum = ep->pipenum;
+
+ if (ep->pipenum == 0) {
+ r8a66597_mdfy(r8a66597, 0, (ISEL | CURPIPE), CFIFOSEL);
+ r8a66597_write(r8a66597, BCLR, ep->fifoctr);
+ pipe_start(r8a66597, pipenum);
+ pipe_irq_enable(r8a66597, pipenum);
+ } else {
+ if (ep->use_dma) {
+ r8a66597_bset(r8a66597, TRCLR, ep->fifosel);
+ pipe_change(r8a66597, pipenum);
+ r8a66597_bset(r8a66597, TRENB, ep->fifosel);
+ r8a66597_write(r8a66597,
+ (req->req.length + ep->ep.maxpacket - 1)
+ / ep->ep.maxpacket,
+ ep->fifotrn);
+ }
+ pipe_start(r8a66597, pipenum); /* trigger once */
+ pipe_irq_enable(r8a66597, pipenum);
+ }
+}
+
+static void start_packet(struct r8a66597_ep *ep, struct r8a66597_request *req)
+{
+ if (ep->desc->bEndpointAddress & USB_DIR_IN)
+ start_packet_write(ep, req);
+ else
+ start_packet_read(ep, req);
+}
+
+static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req)
+{
+ u16 ctsq;
+
+ ctsq = r8a66597_read(ep->r8a66597, INTSTS0) & CTSQ;
+
+ switch (ctsq) {
+ case CS_RDDS:
+ start_ep0_write(ep, req);
+ break;
+ case CS_WRDS:
+ start_packet_read(ep, req);
+ break;
+
+ case CS_WRND:
+ control_end(ep->r8a66597, 0);
+ break;
+ default:
+ printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq);
+ break;
+ }
+}
+
+static void init_controller(struct r8a66597 *r8a66597)
+{
+ u16 vif = r8a66597->pdata->vif ? LDRV : 0;
+ u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0;
+ u16 endian = r8a66597->pdata->endian ? BIGEND : 0;
+
+ if (r8a66597->pdata->on_chip) {
+ r8a66597_bset(r8a66597, 0x04, SYSCFG1);
+ r8a66597_bset(r8a66597, HSE, SYSCFG0);
+
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+ r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+ r8a66597_bset(r8a66597, SCKE, SYSCFG0);
+
+ r8a66597_bset(r8a66597, irq_sense, INTENB1);
+ r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR,
+ DMA0CFG);
+ } else {
+ r8a66597_bset(r8a66597, vif | endian, PINCFG);
+ r8a66597_bset(r8a66597, HSE, SYSCFG0); /* High spd */
+ r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata),
+ XTAL, SYSCFG0);
+
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+ r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+ r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+
+ msleep(3);
+
+ r8a66597_bset(r8a66597, PLLC, SYSCFG0);
+
+ msleep(1);
+
+ r8a66597_bset(r8a66597, SCKE, SYSCFG0);
+
+ r8a66597_bset(r8a66597, irq_sense, INTENB1);
+ r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR,
+ DMA0CFG);
+ }
+}
+
+static void disable_controller(struct r8a66597 *r8a66597)
+{
+ if (r8a66597->pdata->on_chip) {
+ r8a66597_bset(r8a66597, SCKE, SYSCFG0);
+
+ /* disable interrupts */
+ r8a66597_write(r8a66597, 0, INTENB0);
+ r8a66597_write(r8a66597, 0, INTENB1);
+ r8a66597_write(r8a66597, 0, BRDYENB);
+ r8a66597_write(r8a66597, 0, BEMPENB);
+ r8a66597_write(r8a66597, 0, NRDYENB);
+
+ /* clear status */
+ r8a66597_write(r8a66597, 0, BRDYSTS);
+ r8a66597_write(r8a66597, 0, NRDYSTS);
+ r8a66597_write(r8a66597, 0, BEMPSTS);
+
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+
+ } else {
+ r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+ udelay(1);
+ r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+ udelay(1);
+ udelay(1);
+ r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+ }
+}
+
+static void r8a66597_start_xclock(struct r8a66597 *r8a66597)
+{
+ u16 tmp;
+
+ if (!r8a66597->pdata->on_chip) {
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (!(tmp & XCKE))
+ r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+ }
+}
+
+static struct r8a66597_request *get_request_from_ep(struct r8a66597_ep *ep)
+{
+ return list_entry(ep->queue.next, struct r8a66597_request, queue);
+}
+
+/*-------------------------------------------------------------------------*/
+static void transfer_complete(struct r8a66597_ep *ep,
+ struct r8a66597_request *req, int status)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+ int restart = 0;
+
+ if (unlikely(ep->pipenum == 0)) {
+ if (ep->internal_ccpl) {
+ ep->internal_ccpl = 0;
+ return;
+ }
+ }
+
+ list_del_init(&req->queue);
+ if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN)
+ req->req.status = -ESHUTDOWN;
+ else
+ req->req.status = status;
+
+ if (!list_empty(&ep->queue))
+ restart = 1;
+
+ spin_unlock(&ep->r8a66597->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&ep->r8a66597->lock);
+
+ if (restart) {
+ req = get_request_from_ep(ep);
+ if (ep->desc)
+ start_packet(ep, req);
+ }
+}
+
+static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req)
+{
+ int i;
+ u16 tmp;
+ unsigned bufsize;
+ size_t size;
+ void *buf;
+ u16 pipenum = ep->pipenum;
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+
+ pipe_change(r8a66597, pipenum);
+ r8a66597_bset(r8a66597, ISEL, ep->fifosel);
+
+ i = 0;
+ do {
+ tmp = r8a66597_read(r8a66597, ep->fifoctr);
+ if (i++ > 100000) {
+ printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus"
+ "conflict. please power off this controller.");
+ return;
+ }
+ ndelay(1);
+ } while ((tmp & FRDY) == 0);
+
+ /* prepare parameters */
+ bufsize = get_buffer_size(r8a66597, pipenum);
+ buf = req->req.buf + req->req.actual;
+ size = min(bufsize, req->req.length - req->req.actual);
+
+ /* write fifo */
+ if (req->req.buf) {
+ if (size > 0)
+ r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size);
+ if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
+ r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
+ }
+
+ /* update parameters */
+ req->req.actual += size;
+
+ /* check transfer finish */
+ if ((!req->req.zero && (req->req.actual == req->req.length))
+ || (size % ep->ep.maxpacket)
+ || (size == 0)) {
+ disable_irq_ready(r8a66597, pipenum);
+ disable_irq_empty(r8a66597, pipenum);
+ } else {
+ disable_irq_ready(r8a66597, pipenum);
+ enable_irq_empty(r8a66597, pipenum);
+ }
+ pipe_start(r8a66597, pipenum);
+}
+
+static void irq_packet_write(struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ u16 tmp;
+ unsigned bufsize;
+ size_t size;
+ void *buf;
+ u16 pipenum = ep->pipenum;
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+
+ pipe_change(r8a66597, pipenum);
+ tmp = r8a66597_read(r8a66597, ep->fifoctr);
+ if (unlikely((tmp & FRDY) == 0)) {
+ pipe_stop(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum);
+ return;
+ }
+
+ /* prepare parameters */
+ bufsize = get_buffer_size(r8a66597, pipenum);
+ buf = req->req.buf + req->req.actual;
+ size = min(bufsize, req->req.length - req->req.actual);
+
+ /* write fifo */
+ if (req->req.buf) {
+ r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size);
+ if ((size == 0)
+ || ((size % ep->ep.maxpacket) != 0)
+ || ((bufsize != ep->ep.maxpacket)
+ && (bufsize > size)))
+ r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
+ }
+
+ /* update parameters */
+ req->req.actual += size;
+ /* check transfer finish */
+ if ((!req->req.zero && (req->req.actual == req->req.length))
+ || (size % ep->ep.maxpacket)
+ || (size == 0)) {
+ disable_irq_ready(r8a66597, pipenum);
+ enable_irq_empty(r8a66597, pipenum);
+ } else {
+ disable_irq_empty(r8a66597, pipenum);
+ pipe_irq_enable(r8a66597, pipenum);
+ }
+}
+
+static void irq_packet_read(struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ u16 tmp;
+ int rcv_len, bufsize, req_len;
+ int size;
+ void *buf;
+ u16 pipenum = ep->pipenum;
+ struct r8a66597 *r8a66597 = ep->r8a66597;
+ int finish = 0;
+
+ pipe_change(r8a66597, pipenum);
+ tmp = r8a66597_read(r8a66597, ep->fifoctr);
+ if (unlikely((tmp & FRDY) == 0)) {
+ req->req.status = -EPIPE;
+ pipe_stop(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ printk(KERN_ERR "read fifo not ready");
+ return;
+ }
+
+ /* prepare parameters */
+ rcv_len = tmp & DTLN;
+ bufsize = get_buffer_size(r8a66597, pipenum);
+
+ buf = req->req.buf + req->req.actual;
+ req_len = req->req.length - req->req.actual;
+ if (rcv_len < bufsize)
+ size = min(rcv_len, req_len);
+ else
+ size = min(bufsize, req_len);
+
+ /* update parameters */
+ req->req.actual += size;
+
+ /* check transfer finish */
+ if ((!req->req.zero && (req->req.actual == req->req.length))
+ || (size % ep->ep.maxpacket)
+ || (size == 0)) {
+ pipe_stop(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ finish = 1;
+ }
+
+ /* read fifo */
+ if (req->req.buf) {
+ if (size == 0)
+ r8a66597_write(r8a66597, BCLR, ep->fifoctr);
+ else
+ r8a66597_read_fifo(r8a66597, ep->fifoaddr, buf, size);
+
+ }
+
+ if ((ep->pipenum != 0) && finish)
+ transfer_complete(ep, req, 0);
+}
+
+static void irq_pipe_ready(struct r8a66597 *r8a66597, u16 status, u16 enb)
+{
+ u16 check;
+ u16 pipenum;
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+
+ if ((status & BRDY0) && (enb & BRDY0)) {
+ r8a66597_write(r8a66597, ~BRDY0, BRDYSTS);
+ r8a66597_mdfy(r8a66597, 0, CURPIPE, CFIFOSEL);
+
+ ep = &r8a66597->ep[0];
+ req = get_request_from_ep(ep);
+ irq_packet_read(ep, req);
+ } else {
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if ((status & check) && (enb & check)) {
+ r8a66597_write(r8a66597, ~check, BRDYSTS);
+ ep = r8a66597->pipenum2ep[pipenum];
+ req = get_request_from_ep(ep);
+ if (ep->desc->bEndpointAddress & USB_DIR_IN)
+ irq_packet_write(ep, req);
+ else
+ irq_packet_read(ep, req);
+ }
+ }
+ }
+}
+
+static void irq_pipe_empty(struct r8a66597 *r8a66597, u16 status, u16 enb)
+{
+ u16 tmp;
+ u16 check;
+ u16 pipenum;
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+
+ if ((status & BEMP0) && (enb & BEMP0)) {
+ r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
+
+ ep = &r8a66597->ep[0];
+ req = get_request_from_ep(ep);
+ irq_ep0_write(ep, req);
+ } else {
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if ((status & check) && (enb & check)) {
+ r8a66597_write(r8a66597, ~check, BEMPSTS);
+ tmp = control_reg_get(r8a66597, pipenum);
+ if ((tmp & INBUFM) == 0) {
+ disable_irq_empty(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ pipe_stop(r8a66597, pipenum);
+ ep = r8a66597->pipenum2ep[pipenum];
+ req = get_request_from_ep(ep);
+ if (!list_empty(&ep->queue))
+ transfer_complete(ep, req, 0);
+ }
+ }
+ }
+ }
+}
+
+static void get_status(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+ struct r8a66597_ep *ep;
+ u16 pid;
+ u16 status = 0;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ status = 1 << USB_DEVICE_SELF_POWERED;
+ break;
+ case USB_RECIP_INTERFACE:
+ status = 0;
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+ pid = control_reg_get_pid(r8a66597, ep->pipenum);
+ if (pid == PID_STALL)
+ status = 1 << USB_ENDPOINT_HALT;
+ else
+ status = 0;
+ break;
+ default:
+ pipe_stall(r8a66597, 0);
+ return; /* exit */
+ }
+
+ r8a66597->ep0_data = cpu_to_le16(status);
+ r8a66597->ep0_req->buf = &r8a66597->ep0_data;
+ r8a66597->ep0_req->length = 2;
+ /* AV: what happens if we get called again before that gets through? */
+ spin_unlock(&r8a66597->lock);
+ r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_KERNEL);
+ spin_lock(&r8a66597->lock);
+}
+
+static void clear_feature(struct r8a66597 *r8a66597,
+ struct usb_ctrlrequest *ctrl)
+{
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ control_end(r8a66597, 1);
+ break;
+ case USB_RECIP_INTERFACE:
+ control_end(r8a66597, 1);
+ break;
+ case USB_RECIP_ENDPOINT: {
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+ ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+ if (!ep->wedge) {
+ pipe_stop(r8a66597, ep->pipenum);
+ control_reg_sqclr(r8a66597, ep->pipenum);
+ spin_unlock(&r8a66597->lock);
+ usb_ep_clear_halt(&ep->ep);
+ spin_lock(&r8a66597->lock);
+ }
+
+ control_end(r8a66597, 1);
+
+ req = get_request_from_ep(ep);
+ if (ep->busy) {
+ ep->busy = 0;
+ if (list_empty(&ep->queue))
+ break;
+ start_packet(ep, req);
+ } else if (!list_empty(&ep->queue))
+ pipe_start(r8a66597, ep->pipenum);
+ }
+ break;
+ default:
+ pipe_stall(r8a66597, 0);
+ break;
+ }
+}
+
+static void set_feature(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl)
+{
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ control_end(r8a66597, 1);
+ break;
+ case USB_RECIP_INTERFACE:
+ control_end(r8a66597, 1);
+ break;
+ case USB_RECIP_ENDPOINT: {
+ struct r8a66597_ep *ep;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+ ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+ pipe_stall(r8a66597, ep->pipenum);
+
+ control_end(r8a66597, 1);
+ }
+ break;
+ default:
+ pipe_stall(r8a66597, 0);
+ break;
+ }
+}
+
+/* if return value is true, call class driver's setup() */
+static int setup_packet(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl)
+{
+ u16 *p = (u16 *)ctrl;
+ unsigned long offset = USBREQ;
+ int i, ret = 0;
+
+ /* read fifo */
+ r8a66597_write(r8a66597, ~VALID, INTSTS0);
+
+ for (i = 0; i < 4; i++)
+ p[i] = r8a66597_read(r8a66597, offset + i*2);
+
+ /* check request */
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ get_status(r8a66597, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ clear_feature(r8a66597, ctrl);
+ break;
+ case USB_REQ_SET_FEATURE:
+ set_feature(r8a66597, ctrl);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ } else
+ ret = 1;
+ return ret;
+}
+
+static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597)
+{
+ u16 speed = get_usb_speed(r8a66597);
+
+ switch (speed) {
+ case HSMODE:
+ r8a66597->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case FSMODE:
+ r8a66597->gadget.speed = USB_SPEED_FULL;
+ break;
+ default:
+ r8a66597->gadget.speed = USB_SPEED_UNKNOWN;
+ printk(KERN_ERR "USB speed unknown\n");
+ }
+}
+
+static void irq_device_state(struct r8a66597 *r8a66597)
+{
+ u16 dvsq;
+
+ dvsq = r8a66597_read(r8a66597, INTSTS0) & DVSQ;
+ r8a66597_write(r8a66597, ~DVST, INTSTS0);
+
+ if (dvsq == DS_DFLT) {
+ /* bus reset */
+ r8a66597->driver->disconnect(&r8a66597->gadget);
+ r8a66597_update_usb_speed(r8a66597);
+ }
+ if (r8a66597->old_dvsq == DS_CNFG && dvsq != DS_CNFG)
+ r8a66597_update_usb_speed(r8a66597);
+ if ((dvsq == DS_CNFG || dvsq == DS_ADDS)
+ && r8a66597->gadget.speed == USB_SPEED_UNKNOWN)
+ r8a66597_update_usb_speed(r8a66597);
+
+ r8a66597->old_dvsq = dvsq;
+}
+
+static void irq_control_stage(struct r8a66597 *r8a66597)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+ struct usb_ctrlrequest ctrl;
+ u16 ctsq;
+
+ ctsq = r8a66597_read(r8a66597, INTSTS0) & CTSQ;
+ r8a66597_write(r8a66597, ~CTRT, INTSTS0);
+
+ switch (ctsq) {
+ case CS_IDST: {
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+ ep = &r8a66597->ep[0];
+ req = get_request_from_ep(ep);
+ transfer_complete(ep, req, 0);
+ }
+ break;
+
+ case CS_RDDS:
+ case CS_WRDS:
+ case CS_WRND:
+ if (setup_packet(r8a66597, &ctrl)) {
+ spin_unlock(&r8a66597->lock);
+ if (r8a66597->driver->setup(&r8a66597->gadget, &ctrl)
+ < 0)
+ pipe_stall(r8a66597, 0);
+ spin_lock(&r8a66597->lock);
+ }
+ break;
+ case CS_RDSS:
+ case CS_WRSS:
+ control_end(r8a66597, 0);
+ break;
+ default:
+ printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq);
+ break;
+ }
+}
+
+static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
+{
+ struct r8a66597 *r8a66597 = _r8a66597;
+ u16 intsts0;
+ u16 intenb0;
+ u16 brdysts, nrdysts, bempsts;
+ u16 brdyenb, nrdyenb, bempenb;
+ u16 savepipe;
+ u16 mask0;
+
+ spin_lock(&r8a66597->lock);
+
+ intsts0 = r8a66597_read(r8a66597, INTSTS0);
+ intenb0 = r8a66597_read(r8a66597, INTENB0);
+
+ savepipe = r8a66597_read(r8a66597, CFIFOSEL);
+
+ mask0 = intsts0 & intenb0;
+ if (mask0) {
+ brdysts = r8a66597_read(r8a66597, BRDYSTS);
+ nrdysts = r8a66597_read(r8a66597, NRDYSTS);
+ bempsts = r8a66597_read(r8a66597, BEMPSTS);
+ brdyenb = r8a66597_read(r8a66597, BRDYENB);
+ nrdyenb = r8a66597_read(r8a66597, NRDYENB);
+ bempenb = r8a66597_read(r8a66597, BEMPENB);
+
+ if (mask0 & VBINT) {
+ r8a66597_write(r8a66597, 0xffff & ~VBINT,
+ INTSTS0);
+ r8a66597_start_xclock(r8a66597);
+
+ /* start vbus sampling */
+ r8a66597->old_vbus = r8a66597_read(r8a66597, INTSTS0)
+ & VBSTS;
+ r8a66597->scount = R8A66597_MAX_SAMPLING;
+
+ mod_timer(&r8a66597->timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ if (intsts0 & DVSQ)
+ irq_device_state(r8a66597);
+
+ if ((intsts0 & BRDY) && (intenb0 & BRDYE)
+ && (brdysts & brdyenb))
+ irq_pipe_ready(r8a66597, brdysts, brdyenb);
+ if ((intsts0 & BEMP) && (intenb0 & BEMPE)
+ && (bempsts & bempenb))
+ irq_pipe_empty(r8a66597, bempsts, bempenb);
+
+ if (intsts0 & CTRT)
+ irq_control_stage(r8a66597);
+ }
+
+ r8a66597_write(r8a66597, savepipe, CFIFOSEL);
+
+ spin_unlock(&r8a66597->lock);
+ return IRQ_HANDLED;
+}
+
+static void r8a66597_timer(unsigned long _r8a66597)
+{
+ struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+ unsigned long flags;
+ u16 tmp;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (r8a66597->scount > 0) {
+ tmp = r8a66597_read(r8a66597, INTSTS0) & VBSTS;
+ if (tmp == r8a66597->old_vbus) {
+ r8a66597->scount--;
+ if (r8a66597->scount == 0) {
+ if (tmp == VBSTS)
+ r8a66597_usb_connect(r8a66597);
+ else
+ r8a66597_usb_disconnect(r8a66597);
+ } else {
+ mod_timer(&r8a66597->timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ } else {
+ r8a66597->scount = R8A66597_MAX_SAMPLING;
+ r8a66597->old_vbus = tmp;
+ mod_timer(&r8a66597->timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ }
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+static int r8a66597_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct r8a66597_ep *ep;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+ return alloc_pipe_config(ep, desc);
+}
+
+static int r8a66597_disable(struct usb_ep *_ep)
+{
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+ BUG_ON(!ep);
+
+ while (!list_empty(&ep->queue)) {
+ req = get_request_from_ep(ep);
+ spin_lock_irqsave(&ep->r8a66597->lock, flags);
+ transfer_complete(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+ }
+
+ pipe_irq_disable(ep->r8a66597, ep->pipenum);
+ return free_pipe_config(ep);
+}
+
+static struct usb_request *r8a66597_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct r8a66597_request *req;
+
+ req = kzalloc(sizeof(struct r8a66597_request), gfp_flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void r8a66597_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct r8a66597_request *req;
+
+ req = container_of(_req, struct r8a66597_request, req);
+ kfree(req);
+}
+
+static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+ unsigned long flags;
+ int request = 0;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+ req = container_of(_req, struct r8a66597_request, req);
+
+ if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&ep->r8a66597->lock, flags);
+
+ if (list_empty(&ep->queue))
+ request = 1;
+
+ list_add_tail(&req->queue, &ep->queue);
+ req->req.actual = 0;
+ req->req.status = -EINPROGRESS;
+
+ if (ep->desc == NULL) /* control */
+ start_ep0(ep, req);
+ else {
+ if (request && !ep->busy)
+ start_packet(ep, req);
+ }
+
+ spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+
+ return 0;
+}
+
+static int r8a66597_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+ req = container_of(_req, struct r8a66597_request, req);
+
+ spin_lock_irqsave(&ep->r8a66597->lock, flags);
+ if (!list_empty(&ep->queue))
+ transfer_complete(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+
+ return 0;
+}
+
+static int r8a66597_set_halt(struct usb_ep *_ep, int value)
+{
+ struct r8a66597_ep *ep;
+ struct r8a66597_request *req;
+ unsigned long flags;
+ int ret = 0;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+ req = get_request_from_ep(ep);
+
+ spin_lock_irqsave(&ep->r8a66597->lock, flags);
+ if (!list_empty(&ep->queue)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ if (value) {
+ ep->busy = 1;
+ pipe_stall(ep->r8a66597, ep->pipenum);
+ } else {
+ ep->busy = 0;
+ ep->wedge = 0;
+ pipe_stop(ep->r8a66597, ep->pipenum);
+ }
+
+out:
+ spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+ return ret;
+}
+
+static int r8a66597_set_wedge(struct usb_ep *_ep)
+{
+ struct r8a66597_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+
+ if (!ep || !ep->desc)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->r8a66597->lock, flags);
+ ep->wedge = 1;
+ spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+
+ return usb_ep_set_halt(_ep);
+}
+
+static void r8a66597_fifo_flush(struct usb_ep *_ep)
+{
+ struct r8a66597_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct r8a66597_ep, ep);
+ spin_lock_irqsave(&ep->r8a66597->lock, flags);
+ if (list_empty(&ep->queue) && !ep->busy) {
+ pipe_stop(ep->r8a66597, ep->pipenum);
+ r8a66597_bclr(ep->r8a66597, BCLR, ep->fifoctr);
+ }
+ spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+}
+
+static struct usb_ep_ops r8a66597_ep_ops = {
+ .enable = r8a66597_enable,
+ .disable = r8a66597_disable,
+
+ .alloc_request = r8a66597_alloc_request,
+ .free_request = r8a66597_free_request,
+
+ .queue = r8a66597_queue,
+ .dequeue = r8a66597_dequeue,
+
+ .set_halt = r8a66597_set_halt,
+ .set_wedge = r8a66597_set_wedge,
+ .fifo_flush = r8a66597_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct r8a66597 *the_controller;
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct r8a66597 *r8a66597 = the_controller;
+ int retval;
+
+ if (!driver
+ || driver->speed != USB_SPEED_HIGH
+ || !driver->bind
+ || !driver->setup)
+ return -EINVAL;
+ if (!r8a66597)
+ return -ENODEV;
+ if (r8a66597->driver)
+ return -EBUSY;
+
+ /* hook up the driver */
+ driver->driver.bus = NULL;
+ r8a66597->driver = driver;
+ r8a66597->gadget.dev.driver = &driver->driver;
+
+ retval = device_add(&r8a66597->gadget.dev);
+ if (retval) {
+ printk(KERN_ERR "device_add error (%d)\n", retval);
+ goto error;
+ }
+
+ retval = driver->bind(&r8a66597->gadget);
+ if (retval) {
+ printk(KERN_ERR "bind to driver error (%d)\n", retval);
+ device_del(&r8a66597->gadget.dev);
+ goto error;
+ }
+
+ r8a66597_bset(r8a66597, VBSE, INTENB0);
+ if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) {
+ r8a66597_start_xclock(r8a66597);
+ /* start vbus sampling */
+ r8a66597->old_vbus = r8a66597_read(r8a66597,
+ INTSTS0) & VBSTS;
+ r8a66597->scount = R8A66597_MAX_SAMPLING;
+ mod_timer(&r8a66597->timer, jiffies + msecs_to_jiffies(50));
+ }
+
+ return 0;
+
+error:
+ r8a66597->driver = NULL;
+ r8a66597->gadget.dev.driver = NULL;
+
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct r8a66597 *r8a66597 = the_controller;
+ unsigned long flags;
+
+ if (driver != r8a66597->driver || !driver->unbind)
+ return -EINVAL;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN)
+ r8a66597_usb_disconnect(r8a66597);
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+
+ r8a66597_bclr(r8a66597, VBSE, INTENB0);
+
+ driver->unbind(&r8a66597->gadget);
+
+ init_controller(r8a66597);
+ disable_controller(r8a66597);
+
+ device_del(&r8a66597->gadget.dev);
+ r8a66597->driver = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------*/
+static int r8a66597_get_frame(struct usb_gadget *_gadget)
+{
+ struct r8a66597 *r8a66597 = gadget_to_r8a66597(_gadget);
+ return r8a66597_read(r8a66597, FRMNUM) & 0x03FF;
+}
+
+static struct usb_gadget_ops r8a66597_gadget_ops = {
+ .get_frame = r8a66597_get_frame,
+};
+
+static int __exit r8a66597_remove(struct platform_device *pdev)
+{
+ struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
+
+ del_timer_sync(&r8a66597->timer);
+ iounmap((void *)r8a66597->reg);
+ free_irq(platform_get_irq(pdev, 0), r8a66597);
+ r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
+#ifdef CONFIG_HAVE_CLK
+ if (r8a66597->pdata->on_chip) {
+ clk_disable(r8a66597->clk);
+ clk_put(r8a66597->clk);
+ }
+#endif
+ kfree(r8a66597);
+ return 0;
+}
+
+static void nop_completion(struct usb_ep *ep, struct usb_request *r)
+{
+}
+
+static int __init r8a66597_probe(struct platform_device *pdev)
+{
+#ifdef CONFIG_HAVE_CLK
+ char clk_name[8];
+#endif
+ struct resource *res, *ires;
+ int irq;
+ void __iomem *reg = NULL;
+ struct r8a66597 *r8a66597 = NULL;
+ int ret = 0;
+ int i;
+ unsigned long irq_trigger;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ printk(KERN_ERR "platform_get_resource error.\n");
+ goto clean_up;
+ }
+
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ irq = ires->start;
+ irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
+
+ if (irq < 0) {
+ ret = -ENODEV;
+ printk(KERN_ERR "platform_get_irq error.\n");
+ goto clean_up;
+ }
+
+ reg = ioremap(res->start, resource_size(res));
+ if (reg == NULL) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "ioremap error.\n");
+ goto clean_up;
+ }
+
+ /* initialize ucd */
+ r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL);
+ if (r8a66597 == NULL) {
+ printk(KERN_ERR "kzalloc error\n");
+ goto clean_up;
+ }
+
+ spin_lock_init(&r8a66597->lock);
+ dev_set_drvdata(&pdev->dev, r8a66597);
+ r8a66597->pdata = pdev->dev.platform_data;
+ r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
+
+ r8a66597->gadget.ops = &r8a66597_gadget_ops;
+ device_initialize(&r8a66597->gadget.dev);
+ dev_set_name(&r8a66597->gadget.dev, "gadget");
+ r8a66597->gadget.is_dualspeed = 1;
+ r8a66597->gadget.dev.parent = &pdev->dev;
+ r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask;
+ r8a66597->gadget.dev.release = pdev->dev.release;
+ r8a66597->gadget.name = udc_name;
+
+ init_timer(&r8a66597->timer);
+ r8a66597->timer.function = r8a66597_timer;
+ r8a66597->timer.data = (unsigned long)r8a66597;
+ r8a66597->reg = (unsigned long)reg;
+
+#ifdef CONFIG_HAVE_CLK
+ if (r8a66597->pdata->on_chip) {
+ snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
+ r8a66597->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(r8a66597->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
+ clk_name);
+ ret = PTR_ERR(r8a66597->clk);
+ goto clean_up;
+ }
+ clk_enable(r8a66597->clk);
+ }
+#endif
+
+ disable_controller(r8a66597); /* make sure controller is disabled */
+
+ ret = request_irq(irq, r8a66597_irq, IRQF_DISABLED | IRQF_SHARED,
+ udc_name, r8a66597);
+ if (ret < 0) {
+ printk(KERN_ERR "request_irq error (%d)\n", ret);
+ goto clean_up2;
+ }
+
+ INIT_LIST_HEAD(&r8a66597->gadget.ep_list);
+ r8a66597->gadget.ep0 = &r8a66597->ep[0].ep;
+ INIT_LIST_HEAD(&r8a66597->gadget.ep0->ep_list);
+ for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) {
+ struct r8a66597_ep *ep = &r8a66597->ep[i];
+
+ if (i != 0) {
+ INIT_LIST_HEAD(&r8a66597->ep[i].ep.ep_list);
+ list_add_tail(&r8a66597->ep[i].ep.ep_list,
+ &r8a66597->gadget.ep_list);
+ }
+ ep->r8a66597 = r8a66597;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->ep.name = r8a66597_ep_name[i];
+ ep->ep.ops = &r8a66597_ep_ops;
+ ep->ep.maxpacket = 512;
+ }
+ r8a66597->ep[0].ep.maxpacket = 64;
+ r8a66597->ep[0].pipenum = 0;
+ r8a66597->ep[0].fifoaddr = CFIFO;
+ r8a66597->ep[0].fifosel = CFIFOSEL;
+ r8a66597->ep[0].fifoctr = CFIFOCTR;
+ r8a66597->ep[0].fifotrn = 0;
+ r8a66597->ep[0].pipectr = get_pipectr_addr(0);
+ r8a66597->pipenum2ep[0] = &r8a66597->ep[0];
+ r8a66597->epaddr2ep[0] = &r8a66597->ep[0];
+
+ the_controller = r8a66597;
+
+ r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep,
+ GFP_KERNEL);
+ if (r8a66597->ep0_req == NULL)
+ goto clean_up3;
+ r8a66597->ep0_req->complete = nop_completion;
+
+ init_controller(r8a66597);
+
+ dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
+ return 0;
+
+clean_up3:
+ free_irq(irq, r8a66597);
+clean_up2:
+#ifdef CONFIG_HAVE_CLK
+ if (r8a66597->pdata->on_chip) {
+ clk_disable(r8a66597->clk);
+ clk_put(r8a66597->clk);
+ }
+#endif
+clean_up:
+ if (r8a66597) {
+ if (r8a66597->ep0_req)
+ r8a66597_free_request(&r8a66597->ep[0].ep,
+ r8a66597->ep0_req);
+ kfree(r8a66597);
+ }
+ if (reg)
+ iounmap(reg);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver r8a66597_driver = {
+ .remove = __exit_p(r8a66597_remove),
+ .driver = {
+ .name = (char *) udc_name,
+ },
+};
+
+static int __init r8a66597_udc_init(void)
+{
+ return platform_driver_probe(&r8a66597_driver, r8a66597_probe);
+}
+module_init(r8a66597_udc_init);
+
+static void __exit r8a66597_udc_cleanup(void)
+{
+ platform_driver_unregister(&r8a66597_driver);
+}
+module_exit(r8a66597_udc_cleanup);
+
+MODULE_DESCRIPTION("R8A66597 USB gadget driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h
new file mode 100644
index 000000000000..03087e7b9190
--- /dev/null
+++ b/drivers/usb/gadget/r8a66597-udc.h
@@ -0,0 +1,256 @@
+/*
+ * 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 wedge: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__ */
+
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 1a920c70b5a1..f21ca7d27a43 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -336,13 +336,6 @@ config USB_R8A66597_HCD
To compile this driver as a module, choose M here: the
module will be called r8a66597-hcd.
-config SUPERH_ON_CHIP_R8A66597
- boolean "Enable SuperH on-chip R8A66597 USB"
- depends on USB_R8A66597_HCD && (CPU_SUBTYPE_SH7366 || CPU_SUBTYPE_SH7723 || CPU_SUBTYPE_SH7724)
- help
- This driver enables support for the on-chip R8A66597 in the
- SH7366, SH7723 and SH7724 processors.
-
config USB_WHCI_HCD
tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)"
depends on EXPERIMENTAL
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index e18f74946e68..749b53742828 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -91,43 +91,43 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
u16 tmp;
int i = 0;
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
-#if defined(CONFIG_HAVE_CLK)
- clk_enable(r8a66597->clk);
+ if (r8a66597->pdata->on_chip) {
+#ifdef CONFIG_HAVE_CLK
+ clk_enable(r8a66597->clk);
#endif
- do {
- r8a66597_write(r8a66597, SCKE, SYSCFG0);
- tmp = r8a66597_read(r8a66597, SYSCFG0);
- if (i++ > 1000) {
- printk(KERN_ERR "r8a66597: register access fail.\n");
- return -ENXIO;
- }
- } while ((tmp & SCKE) != SCKE);
- r8a66597_write(r8a66597, 0x04, 0x02);
-#else
- do {
- r8a66597_write(r8a66597, USBE, SYSCFG0);
- tmp = r8a66597_read(r8a66597, SYSCFG0);
- if (i++ > 1000) {
- printk(KERN_ERR "r8a66597: register access fail.\n");
- return -ENXIO;
- }
- } while ((tmp & USBE) != USBE);
- r8a66597_bclr(r8a66597, USBE, SYSCFG0);
- r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata), XTAL,
- SYSCFG0);
+ do {
+ r8a66597_write(r8a66597, SCKE, SYSCFG0);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 1000) {
+ printk(KERN_ERR "r8a66597: reg access fail.\n");
+ return -ENXIO;
+ }
+ } while ((tmp & SCKE) != SCKE);
+ r8a66597_write(r8a66597, 0x04, 0x02);
+ } else {
+ do {
+ r8a66597_write(r8a66597, USBE, SYSCFG0);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 1000) {
+ printk(KERN_ERR "r8a66597: reg access fail.\n");
+ return -ENXIO;
+ }
+ } while ((tmp & USBE) != USBE);
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata),
+ XTAL, SYSCFG0);
- i = 0;
- r8a66597_bset(r8a66597, XCKE, SYSCFG0);
- do {
- msleep(1);
- tmp = r8a66597_read(r8a66597, SYSCFG0);
- if (i++ > 500) {
- printk(KERN_ERR "r8a66597: register access fail.\n");
- return -ENXIO;
- }
- } while ((tmp & SCKE) != SCKE);
-#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
+ i = 0;
+ r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+ do {
+ msleep(1);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 500) {
+ printk(KERN_ERR "r8a66597: reg access fail.\n");
+ return -ENXIO;
+ }
+ } while ((tmp & SCKE) != SCKE);
+ }
return 0;
}
@@ -136,15 +136,16 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
{
r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
udelay(1);
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
-#if defined(CONFIG_HAVE_CLK)
- clk_disable(r8a66597->clk);
-#endif
-#else
- r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
- r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
- r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+
+ if (r8a66597->pdata->on_chip) {
+#ifdef CONFIG_HAVE_CLK
+ clk_disable(r8a66597->clk);
#endif
+ } else {
+ r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+ r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ }
}
static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port)
@@ -205,7 +206,7 @@ static int enable_controller(struct r8a66597 *r8a66597)
r8a66597_bset(r8a66597, SIGNE | SACKE, INTENB1);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
+ for (port = 0; port < r8a66597->max_root_hub; port++)
r8a66597_enable_port(r8a66597, port);
return 0;
@@ -218,7 +219,7 @@ static void disable_controller(struct r8a66597 *r8a66597)
r8a66597_write(r8a66597, 0, INTENB0);
r8a66597_write(r8a66597, 0, INTSTS0);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
+ for (port = 0; port < r8a66597->max_root_hub; port++)
r8a66597_disable_port(r8a66597, port);
r8a66597_clock_disable(r8a66597);
@@ -249,11 +250,12 @@ static int is_hub_limit(char *devpath)
return ((strlen(devpath) >= 4) ? 1 : 0);
}
-static void get_port_number(char *devpath, u16 *root_port, u16 *hub_port)
+static void get_port_number(struct r8a66597 *r8a66597,
+ char *devpath, u16 *root_port, u16 *hub_port)
{
if (root_port) {
*root_port = (devpath[0] & 0x0F) - 1;
- if (*root_port >= R8A66597_MAX_ROOT_HUB)
+ if (*root_port >= r8a66597->max_root_hub)
printk(KERN_ERR "r8a66597: Illegal root port number.\n");
}
if (hub_port)
@@ -355,7 +357,8 @@ static int make_r8a66597_device(struct r8a66597 *r8a66597,
INIT_LIST_HEAD(&dev->device_list);
list_add_tail(&dev->device_list, &r8a66597->child_device);
- get_port_number(urb->dev->devpath, &dev->root_port, &dev->hub_port);
+ get_port_number(r8a66597, urb->dev->devpath,
+ &dev->root_port, &dev->hub_port);
if (!is_child_device(urb->dev->devpath))
r8a66597->root_hub[dev->root_port].dev = dev;
@@ -420,7 +423,7 @@ static void free_usb_address(struct r8a66597 *r8a66597,
list_del(&dev->device_list);
kfree(dev);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
if (r8a66597->root_hub[port].dev == dev) {
r8a66597->root_hub[port].dev = NULL;
break;
@@ -495,10 +498,20 @@ static void r8a66597_pipe_toggle(struct r8a66597 *r8a66597,
r8a66597_bset(r8a66597, SQCLR, pipe->pipectr);
}
+static inline unsigned short mbw_value(struct r8a66597 *r8a66597)
+{
+ if (r8a66597->pdata->on_chip)
+ return MBW_32;
+ else
+ return MBW_16;
+}
+
/* this function must be called with interrupt disabled */
static inline void cfifo_change(struct r8a66597 *r8a66597, u16 pipenum)
{
- r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL);
+ unsigned short mbw = mbw_value(r8a66597);
+
+ r8a66597_mdfy(r8a66597, mbw | pipenum, mbw | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
}
@@ -506,11 +519,13 @@ static inline void cfifo_change(struct r8a66597 *r8a66597, u16 pipenum)
static inline void fifo_change_from_pipe(struct r8a66597 *r8a66597,
struct r8a66597_pipe *pipe)
{
+ unsigned short mbw = mbw_value(r8a66597);
+
cfifo_change(r8a66597, 0);
- r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D0FIFOSEL);
- r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D1FIFOSEL);
+ r8a66597_mdfy(r8a66597, mbw | 0, mbw | CURPIPE, D0FIFOSEL);
+ r8a66597_mdfy(r8a66597, mbw | 0, mbw | CURPIPE, D1FIFOSEL);
- r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum, MBW | CURPIPE,
+ r8a66597_mdfy(r8a66597, mbw | pipe->info.pipenum, mbw | CURPIPE,
pipe->fifosel);
r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE, pipe->info.pipenum);
}
@@ -742,9 +757,13 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
struct r8a66597_pipe *pipe,
struct urb *urb)
{
-#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
int i;
struct r8a66597_pipe_info *info = &pipe->info;
+ unsigned short mbw = mbw_value(r8a66597);
+
+ /* pipe dma is only for external controlles */
+ if (r8a66597->pdata->on_chip)
+ return;
if ((pipe->info.pipenum != 0) && (info->type != R8A66597_INT)) {
for (i = 0; i < R8A66597_MAX_DMA_CHANNEL; i++) {
@@ -763,8 +782,8 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
set_pipe_reg_addr(pipe, i);
cfifo_change(r8a66597, 0);
- r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum,
- MBW | CURPIPE, pipe->fifosel);
+ r8a66597_mdfy(r8a66597, mbw | pipe->info.pipenum,
+ mbw | CURPIPE, pipe->fifosel);
r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE,
pipe->info.pipenum);
@@ -772,7 +791,6 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
break;
}
}
-#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
}
/* this function must be called with interrupt disabled */
@@ -1769,7 +1787,7 @@ static void r8a66597_timer(unsigned long _r8a66597)
spin_lock_irqsave(&r8a66597->lock, flags);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
+ for (port = 0; port < r8a66597->max_root_hub; port++)
r8a66597_root_hub_control(r8a66597, port);
spin_unlock_irqrestore(&r8a66597->lock, flags);
@@ -1807,7 +1825,7 @@ static void set_address_zero(struct r8a66597 *r8a66597, struct urb *urb)
u16 root_port, hub_port;
if (usb_address == 0) {
- get_port_number(urb->dev->devpath,
+ get_port_number(r8a66597, urb->dev->devpath,
&root_port, &hub_port);
set_devadd_reg(r8a66597, 0,
get_r8a66597_usb_speed(urb->dev->speed),
@@ -2082,7 +2100,7 @@ static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf)
*buf = 0; /* initialize (no change) */
- for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) {
+ for (i = 0; i < r8a66597->max_root_hub; i++) {
if (r8a66597->root_hub[i].port & 0xffff0000)
*buf |= 1 << (i + 1);
}
@@ -2097,11 +2115,11 @@ static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
{
desc->bDescriptorType = 0x29;
desc->bHubContrCurrent = 0;
- desc->bNbrPorts = R8A66597_MAX_ROOT_HUB;
+ desc->bNbrPorts = r8a66597->max_root_hub;
desc->bDescLength = 9;
desc->bPwrOn2PwrGood = 0;
desc->wHubCharacteristics = cpu_to_le16(0x0011);
- desc->bitmap[0] = ((1 << R8A66597_MAX_ROOT_HUB) - 1) << 1;
+ desc->bitmap[0] = ((1 << r8a66597->max_root_hub) - 1) << 1;
desc->bitmap[1] = ~0;
}
@@ -2129,7 +2147,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
break;
case ClearPortFeature:
- if (wIndex > R8A66597_MAX_ROOT_HUB)
+ if (wIndex > r8a66597->max_root_hub)
goto error;
if (wLength != 0)
goto error;
@@ -2162,12 +2180,12 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
*buf = 0x00;
break;
case GetPortStatus:
- if (wIndex > R8A66597_MAX_ROOT_HUB)
+ if (wIndex > r8a66597->max_root_hub)
goto error;
*(__le32 *)buf = cpu_to_le32(rh->port);
break;
case SetPortFeature:
- if (wIndex > R8A66597_MAX_ROOT_HUB)
+ if (wIndex > r8a66597->max_root_hub)
goto error;
if (wLength != 0)
goto error;
@@ -2216,7 +2234,7 @@ static int r8a66597_bus_suspend(struct usb_hcd *hcd)
dbg("%s", __func__);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
unsigned long dvstctr_reg = get_dvstctr_reg(port);
@@ -2247,7 +2265,7 @@ static int r8a66597_bus_resume(struct usb_hcd *hcd)
dbg("%s", __func__);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
unsigned long dvstctr_reg = get_dvstctr_reg(port);
@@ -2305,16 +2323,16 @@ static struct hc_driver r8a66597_hc_driver = {
};
#if defined(CONFIG_PM)
-static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
+static int r8a66597_suspend(struct device *dev)
{
- struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
+ struct r8a66597 *r8a66597 = dev_get_drvdata(dev);
int port;
dbg("%s", __func__);
disable_controller(r8a66597);
- for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
+ for (port = 0; port < r8a66597->max_root_hub; port++) {
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
rh->port = 0x00000000;
@@ -2323,9 +2341,9 @@ static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int r8a66597_resume(struct platform_device *pdev)
+static int r8a66597_resume(struct device *dev)
{
- struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
+ struct r8a66597 *r8a66597 = dev_get_drvdata(dev);
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
dbg("%s", __func__);
@@ -2335,9 +2353,17 @@ static int r8a66597_resume(struct platform_device *pdev)
return 0;
}
+
+static struct dev_pm_ops r8a66597_dev_pm_ops = {
+ .suspend = r8a66597_suspend,
+ .resume = r8a66597_resume,
+ .poweroff = r8a66597_suspend,
+ .restore = r8a66597_resume,
+};
+
+#define R8A66597_DEV_PM_OPS (&r8a66597_dev_pm_ops)
#else /* if defined(CONFIG_PM) */
-#define r8a66597_suspend NULL
-#define r8a66597_resume NULL
+#define R8A66597_DEV_PM_OPS NULL
#endif
static int __init_or_module r8a66597_remove(struct platform_device *pdev)
@@ -2348,8 +2374,9 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
del_timer_sync(&r8a66597->rh_timer);
usb_remove_hcd(hcd);
iounmap((void *)r8a66597->reg);
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
- clk_put(r8a66597->clk);
+#ifdef CONFIG_HAVE_CLK
+ if (r8a66597->pdata->on_chip)
+ clk_put(r8a66597->clk);
#endif
usb_put_hcd(hcd);
return 0;
@@ -2357,7 +2384,7 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
static int __devinit r8a66597_probe(struct platform_device *pdev)
{
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
char clk_name[8];
#endif
struct resource *res = NULL, *ires;
@@ -2419,15 +2446,20 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
r8a66597->pdata = pdev->dev.platform_data;
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
- snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
- r8a66597->clk = clk_get(&pdev->dev, clk_name);
- if (IS_ERR(r8a66597->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
- ret = PTR_ERR(r8a66597->clk);
- goto clean_up2;
- }
+ if (r8a66597->pdata->on_chip) {
+#ifdef CONFIG_HAVE_CLK
+ snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
+ r8a66597->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(r8a66597->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
+ clk_name);
+ ret = PTR_ERR(r8a66597->clk);
+ goto clean_up2;
+ }
#endif
+ r8a66597->max_root_hub = 1;
+ } else
+ r8a66597->max_root_hub = 2;
spin_lock_init(&r8a66597->lock);
init_timer(&r8a66597->rh_timer);
@@ -2457,8 +2489,9 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
return 0;
clean_up3:
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
- clk_put(r8a66597->clk);
+#ifdef CONFIG_HAVE_CLK
+ if (r8a66597->pdata->on_chip)
+ clk_put(r8a66597->clk);
clean_up2:
#endif
usb_put_hcd(hcd);
@@ -2473,11 +2506,10 @@ clean_up:
static struct platform_driver r8a66597_driver = {
.probe = r8a66597_probe,
.remove = r8a66597_remove,
- .suspend = r8a66597_suspend,
- .resume = r8a66597_resume,
.driver = {
.name = (char *) hcd_name,
.owner = THIS_MODULE,
+ .pm = R8A66597_DEV_PM_OPS,
},
};
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
index d72680b433f9..228e3fb23854 100644
--- a/drivers/usb/host/r8a66597.h
+++ b/drivers/usb/host/r8a66597.h
@@ -26,390 +26,16 @@
#ifndef __R8A66597_H__
#define __R8A66597_H__
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
#endif
#include <linux/usb/r8a66597.h>
-#define SYSCFG0 0x00
-#define SYSCFG1 0x02
-#define SYSSTS0 0x04
-#define SYSSTS1 0x06
-#define DVSTCTR0 0x08
-#define DVSTCTR1 0x0A
-#define TESTMODE 0x0C
-#define PINCFG 0x0E
-#define DMA0CFG 0x10
-#define DMA1CFG 0x12
-#define CFIFO 0x14
-#define D0FIFO 0x18
-#define D1FIFO 0x1C
-#define CFIFOSEL 0x20
-#define CFIFOCTR 0x22
-#define CFIFOSIE 0x24
-#define D0FIFOSEL 0x28
-#define D0FIFOCTR 0x2A
-#define D1FIFOSEL 0x2C
-#define D1FIFOCTR 0x2E
-#define INTENB0 0x30
-#define INTENB1 0x32
-#define INTENB2 0x34
-#define BRDYENB 0x36
-#define NRDYENB 0x38
-#define BEMPENB 0x3A
-#define SOFCFG 0x3C
-#define INTSTS0 0x40
-#define INTSTS1 0x42
-#define INTSTS2 0x44
-#define BRDYSTS 0x46
-#define NRDYSTS 0x48
-#define BEMPSTS 0x4A
-#define FRMNUM 0x4C
-#define UFRMNUM 0x4E
-#define USBADDR 0x50
-#define USBREQ 0x54
-#define USBVAL 0x56
-#define USBINDX 0x58
-#define USBLENG 0x5A
-#define DCPCFG 0x5C
-#define DCPMAXP 0x5E
-#define DCPCTR 0x60
-#define PIPESEL 0x64
-#define PIPECFG 0x68
-#define PIPEBUF 0x6A
-#define PIPEMAXP 0x6C
-#define PIPEPERI 0x6E
-#define PIPE1CTR 0x70
-#define PIPE2CTR 0x72
-#define PIPE3CTR 0x74
-#define PIPE4CTR 0x76
-#define PIPE5CTR 0x78
-#define PIPE6CTR 0x7A
-#define PIPE7CTR 0x7C
-#define PIPE8CTR 0x7E
-#define PIPE9CTR 0x80
-#define PIPE1TRE 0x90
-#define PIPE1TRN 0x92
-#define PIPE2TRE 0x94
-#define PIPE2TRN 0x96
-#define PIPE3TRE 0x98
-#define PIPE3TRN 0x9A
-#define PIPE4TRE 0x9C
-#define PIPE4TRN 0x9E
-#define PIPE5TRE 0xA0
-#define PIPE5TRN 0xA2
-#define DEVADD0 0xD0
-#define DEVADD1 0xD2
-#define DEVADD2 0xD4
-#define DEVADD3 0xD6
-#define DEVADD4 0xD8
-#define DEVADD5 0xDA
-#define DEVADD6 0xDC
-#define DEVADD7 0xDE
-#define DEVADD8 0xE0
-#define DEVADD9 0xE2
-#define DEVADDA 0xE4
-
-/* System Configuration Control Register */
-#define XTAL 0xC000 /* b15-14: Crystal selection */
-#define XTAL48 0x8000 /* 48MHz */
-#define XTAL24 0x4000 /* 24MHz */
-#define XTAL12 0x0000 /* 12MHz */
-#define XCKE 0x2000 /* b13: External clock enable */
-#define PLLC 0x0800 /* b11: PLL control */
-#define SCKE 0x0400 /* b10: USB clock enable */
-#define PCSDIS 0x0200 /* b9: not CS wakeup */
-#define LPSME 0x0100 /* b8: Low power sleep mode */
-#define HSE 0x0080 /* b7: Hi-speed enable */
-#define DCFM 0x0040 /* b6: Controller function select */
-#define DRPD 0x0020 /* b5: D+/- pull down control */
-#define DPRPU 0x0010 /* b4: D+ pull up control */
-#define USBE 0x0001 /* b0: USB module operation enable */
-
-/* System Configuration Status Register */
-#define OVCBIT 0x8000 /* b15-14: Over-current bit */
-#define OVCMON 0xC000 /* b15-14: Over-current monitor */
-#define SOFEA 0x0020 /* b5: SOF monitor */
-#define IDMON 0x0004 /* b3: ID-pin monitor */
-#define LNST 0x0003 /* b1-0: D+, D- line status */
-#define SE1 0x0003 /* SE1 */
-#define FS_KSTS 0x0002 /* Full-Speed K State */
-#define FS_JSTS 0x0001 /* Full-Speed J State */
-#define LS_JSTS 0x0002 /* Low-Speed J State */
-#define LS_KSTS 0x0001 /* Low-Speed K State */
-#define SE0 0x0000 /* SE0 */
-
-/* Device State Control Register */
-#define EXTLP0 0x0400 /* b10: External port */
-#define VBOUT 0x0200 /* b9: VBUS output */
-#define WKUP 0x0100 /* b8: Remote wakeup */
-#define RWUPE 0x0080 /* b7: Remote wakeup sense */
-#define USBRST 0x0040 /* b6: USB reset enable */
-#define RESUME 0x0020 /* b5: Resume enable */
-#define UACT 0x0010 /* b4: USB bus enable */
-#define RHST 0x0007 /* b1-0: Reset handshake status */
-#define HSPROC 0x0004 /* HS handshake is processing */
-#define HSMODE 0x0003 /* Hi-Speed mode */
-#define FSMODE 0x0002 /* Full-Speed mode */
-#define LSMODE 0x0001 /* Low-Speed mode */
-#define UNDECID 0x0000 /* Undecided */
-
-/* Test Mode Register */
-#define UTST 0x000F /* b3-0: Test select */
-#define H_TST_PACKET 0x000C /* HOST TEST Packet */
-#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
-#define H_TST_K 0x000A /* HOST TEST K */
-#define H_TST_J 0x0009 /* HOST TEST J */
-#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */
-#define P_TST_PACKET 0x0004 /* PERI TEST Packet */
-#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
-#define P_TST_K 0x0002 /* PERI TEST K */
-#define P_TST_J 0x0001 /* PERI TEST J */
-#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */
-
-/* Data Pin Configuration Register */
-#define LDRV 0x8000 /* b15: Drive Current Adjust */
-#define VIF1 0x0000 /* VIF = 1.8V */
-#define VIF3 0x8000 /* VIF = 3.3V */
-#define INTA 0x0001 /* b1: USB INT-pin active */
-
-/* DMAx Pin Configuration Register */
-#define DREQA 0x4000 /* b14: Dreq active select */
-#define BURST 0x2000 /* b13: Burst mode */
-#define DACKA 0x0400 /* b10: Dack active select */
-#define DFORM 0x0380 /* b9-7: DMA mode select */
-#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
-#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
-#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
-#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
-#define DENDA 0x0040 /* b6: Dend active select */
-#define PKTM 0x0020 /* b5: Packet mode */
-#define DENDE 0x0010 /* b4: Dend enable */
-#define OBUS 0x0004 /* b2: OUTbus mode */
-
-/* CFIFO/DxFIFO Port Select Register */
-#define RCNT 0x8000 /* b15: Read count mode */
-#define REW 0x4000 /* b14: Buffer rewind */
-#define DCLRM 0x2000 /* b13: DMA buffer clear mode */
-#define DREQE 0x1000 /* b12: DREQ output enable */
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
-#define MBW 0x0800
-#else
-#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */
-#endif
-#define MBW_8 0x0000 /* 8bit */
-#define MBW_16 0x0400 /* 16bit */
-#define BIGEND 0x0100 /* b8: Big endian mode */
-#define BYTE_LITTLE 0x0000 /* little dendian */
-#define BYTE_BIG 0x0100 /* big endifan */
-#define ISEL 0x0020 /* b5: DCP FIFO port direction select */
-#define CURPIPE 0x000F /* b2-0: PIPE select */
-
-/* CFIFO/DxFIFO Port Control Register */
-#define BVAL 0x8000 /* b15: Buffer valid flag */
-#define BCLR 0x4000 /* b14: Buffer clear */
-#define FRDY 0x2000 /* b13: FIFO ready */
-#define DTLN 0x0FFF /* b11-0: FIFO received data length */
-
-/* Interrupt Enable Register 0 */
-#define VBSE 0x8000 /* b15: VBUS interrupt */
-#define RSME 0x4000 /* b14: Resume interrupt */
-#define SOFE 0x2000 /* b13: Frame update interrupt */
-#define DVSE 0x1000 /* b12: Device state transition interrupt */
-#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */
-#define BEMPE 0x0400 /* b10: Buffer empty interrupt */
-#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */
-#define BRDYE 0x0100 /* b8: Buffer ready interrupt */
-
-/* Interrupt Enable Register 1 */
-#define OVRCRE 0x8000 /* b15: Over-current interrupt */
-#define BCHGE 0x4000 /* b14: USB us chenge interrupt */
-#define DTCHE 0x1000 /* b12: Detach sense interrupt */
-#define ATTCHE 0x0800 /* b11: Attach sense interrupt */
-#define EOFERRE 0x0040 /* b6: EOF error interrupt */
-#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
-#define SACKE 0x0010 /* b4: SETUP ACK interrupt */
-
-/* BRDY Interrupt Enable/Status Register */
-#define BRDY9 0x0200 /* b9: PIPE9 */
-#define BRDY8 0x0100 /* b8: PIPE8 */
-#define BRDY7 0x0080 /* b7: PIPE7 */
-#define BRDY6 0x0040 /* b6: PIPE6 */
-#define BRDY5 0x0020 /* b5: PIPE5 */
-#define BRDY4 0x0010 /* b4: PIPE4 */
-#define BRDY3 0x0008 /* b3: PIPE3 */
-#define BRDY2 0x0004 /* b2: PIPE2 */
-#define BRDY1 0x0002 /* b1: PIPE1 */
-#define BRDY0 0x0001 /* b1: PIPE0 */
-
-/* NRDY Interrupt Enable/Status Register */
-#define NRDY9 0x0200 /* b9: PIPE9 */
-#define NRDY8 0x0100 /* b8: PIPE8 */
-#define NRDY7 0x0080 /* b7: PIPE7 */
-#define NRDY6 0x0040 /* b6: PIPE6 */
-#define NRDY5 0x0020 /* b5: PIPE5 */
-#define NRDY4 0x0010 /* b4: PIPE4 */
-#define NRDY3 0x0008 /* b3: PIPE3 */
-#define NRDY2 0x0004 /* b2: PIPE2 */
-#define NRDY1 0x0002 /* b1: PIPE1 */
-#define NRDY0 0x0001 /* b1: PIPE0 */
-
-/* BEMP Interrupt Enable/Status Register */
-#define BEMP9 0x0200 /* b9: PIPE9 */
-#define BEMP8 0x0100 /* b8: PIPE8 */
-#define BEMP7 0x0080 /* b7: PIPE7 */
-#define BEMP6 0x0040 /* b6: PIPE6 */
-#define BEMP5 0x0020 /* b5: PIPE5 */
-#define BEMP4 0x0010 /* b4: PIPE4 */
-#define BEMP3 0x0008 /* b3: PIPE3 */
-#define BEMP2 0x0004 /* b2: PIPE2 */
-#define BEMP1 0x0002 /* b1: PIPE1 */
-#define BEMP0 0x0001 /* b0: PIPE0 */
-
-/* SOF Pin Configuration Register */
-#define TRNENSEL 0x0100 /* b8: Select transaction enable period */
-#define BRDYM 0x0040 /* b6: BRDY clear timing */
-#define INTL 0x0020 /* b5: Interrupt sense select */
-#define EDGESTS 0x0010 /* b4: */
-#define SOFMODE 0x000C /* b3-2: SOF pin select */
-#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */
-#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
-#define SOF_DISABLE 0x0000 /* SOF OUT Disable */
-
-/* Interrupt Status Register 0 */
-#define VBINT 0x8000 /* b15: VBUS interrupt */
-#define RESM 0x4000 /* b14: Resume interrupt */
-#define SOFR 0x2000 /* b13: SOF frame update interrupt */
-#define DVST 0x1000 /* b12: Device state transition interrupt */
-#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */
-#define BEMP 0x0400 /* b10: Buffer empty interrupt */
-#define NRDY 0x0200 /* b9: Buffer not ready interrupt */
-#define BRDY 0x0100 /* b8: Buffer ready interrupt */
-#define VBSTS 0x0080 /* b7: VBUS input port */
-#define DVSQ 0x0070 /* b6-4: Device state */
-#define DS_SPD_CNFG 0x0070 /* Suspend Configured */
-#define DS_SPD_ADDR 0x0060 /* Suspend Address */
-#define DS_SPD_DFLT 0x0050 /* Suspend Default */
-#define DS_SPD_POWR 0x0040 /* Suspend Powered */
-#define DS_SUSP 0x0040 /* Suspend */
-#define DS_CNFG 0x0030 /* Configured */
-#define DS_ADDS 0x0020 /* Address */
-#define DS_DFLT 0x0010 /* Default */
-#define DS_POWR 0x0000 /* Powered */
-#define DVSQS 0x0030 /* b5-4: Device state */
-#define VALID 0x0008 /* b3: Setup packet detected flag */
-#define CTSQ 0x0007 /* b2-0: Control transfer stage */
-#define CS_SQER 0x0006 /* Sequence error */
-#define CS_WRND 0x0005 /* Control write nodata status stage */
-#define CS_WRSS 0x0004 /* Control write status stage */
-#define CS_WRDS 0x0003 /* Control write data stage */
-#define CS_RDSS 0x0002 /* Control read status stage */
-#define CS_RDDS 0x0001 /* Control read data stage */
-#define CS_IDST 0x0000 /* Idle or setup stage */
-
-/* Interrupt Status Register 1 */
-#define OVRCR 0x8000 /* b15: Over-current interrupt */
-#define BCHG 0x4000 /* b14: USB bus chenge interrupt */
-#define DTCH 0x1000 /* b12: Detach sense interrupt */
-#define ATTCH 0x0800 /* b11: Attach sense interrupt */
-#define EOFERR 0x0040 /* b6: EOF-error interrupt */
-#define SIGN 0x0020 /* b5: Setup ignore interrupt */
-#define SACK 0x0010 /* b4: Setup acknowledge interrupt */
-
-/* Frame Number Register */
-#define OVRN 0x8000 /* b15: Overrun error */
-#define CRCE 0x4000 /* b14: Received data error */
-#define FRNM 0x07FF /* b10-0: Frame number */
-
-/* Micro Frame Number Register */
-#define UFRNM 0x0007 /* b2-0: Micro frame number */
-
-/* Default Control Pipe Maxpacket Size Register */
-/* Pipe Maxpacket Size Register */
-#define DEVSEL 0xF000 /* b15-14: Device address select */
-#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */
-
-/* Default Control Pipe Control Register */
-#define BSTS 0x8000 /* b15: Buffer status */
-#define SUREQ 0x4000 /* b14: Send USB request */
-#define CSCLR 0x2000 /* b13: complete-split status clear */
-#define CSSTS 0x1000 /* b12: complete-split status */
-#define SUREQCLR 0x0800 /* b11: stop setup request */
-#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
-#define SQSET 0x0080 /* b7: Sequence toggle bit set */
-#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
-#define PBUSY 0x0020 /* b5: pipe busy */
-#define PINGE 0x0010 /* b4: ping enable */
-#define CCPL 0x0004 /* b2: Enable control transfer complete */
-#define PID 0x0003 /* b1-0: Response PID */
-#define PID_STALL11 0x0003 /* STALL */
-#define PID_STALL 0x0002 /* STALL */
-#define PID_BUF 0x0001 /* BUF */
-#define PID_NAK 0x0000 /* NAK */
-
-/* Pipe Window Select Register */
-#define PIPENM 0x0007 /* b2-0: Pipe select */
-
-/* Pipe Configuration Register */
-#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */
-#define R8A66597_ISO 0xC000 /* Isochronous */
-#define R8A66597_INT 0x8000 /* Interrupt */
-#define R8A66597_BULK 0x4000 /* Bulk */
-#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */
-#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */
-#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */
-#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */
-#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */
-#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */
-
-/* Pipe Buffer Configuration Register */
-#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
-#define BUFNMB 0x007F /* b6-0: Pipe buffer number */
-#define PIPE0BUF 256
-#define PIPExBUF 64
-
-/* Pipe Maxpacket Size Register */
-#define MXPS 0x07FF /* b10-0: Maxpacket size */
-
-/* Pipe Cycle Configuration Register */
-#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */
-#define IITV 0x0007 /* b2-0: Isochronous interval */
-
-/* Pipex Control Register */
-#define BSTS 0x8000 /* b15: Buffer status */
-#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */
-#define CSCLR 0x2000 /* b13: complete-split status clear */
-#define CSSTS 0x1000 /* b12: complete-split status */
-#define ATREPM 0x0400 /* b10: Auto repeat mode */
-#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */
-#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
-#define SQSET 0x0080 /* b7: Sequence toggle bit set */
-#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
-#define PBUSY 0x0020 /* b5: pipe busy */
-#define PID 0x0003 /* b1-0: Response PID */
-
-/* PIPExTRE */
-#define TRENB 0x0200 /* b9: Transaction counter enable */
-#define TRCLR 0x0100 /* b8: Transaction counter clear */
-
-/* PIPExTRN */
-#define TRNCNT 0xFFFF /* b15-0: Transaction counter */
-
-/* DEVADDx */
-#define UPPHUB 0x7800
-#define HUBPORT 0x0700
-#define USBSPD 0x00C0
-#define RTPORT 0x0001
-
#define R8A66597_MAX_NUM_PIPE 10
#define R8A66597_BUF_BSIZE 8
#define R8A66597_MAX_DEVICE 10
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
-#define R8A66597_MAX_ROOT_HUB 1
-#else
#define R8A66597_MAX_ROOT_HUB 2
-#endif
#define R8A66597_MAX_SAMPLING 5
#define R8A66597_RH_POLL_TIME 10
#define R8A66597_MAX_DMA_CHANNEL 2
@@ -487,7 +113,7 @@ struct r8a66597_root_hub {
struct r8a66597 {
spinlock_t lock;
unsigned long reg;
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
struct clk *clk;
#endif
struct r8a66597_platdata *pdata;
@@ -504,6 +130,7 @@ struct r8a66597 {
unsigned short interval_map;
unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
unsigned char dma_map;
+ unsigned int max_root_hub;
struct list_head child_device;
unsigned long child_connect_map[4];
@@ -550,21 +177,22 @@ static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
unsigned long offset, u16 *buf,
int len)
{
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
unsigned long fifoaddr = r8a66597->reg + offset;
unsigned long count;
- count = len / 4;
- insl(fifoaddr, buf, count);
+ if (r8a66597->pdata->on_chip) {
+ count = len / 4;
+ insl(fifoaddr, buf, count);
- if (len & 0x00000003) {
- unsigned long tmp = inl(fifoaddr);
- memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03);
+ if (len & 0x00000003) {
+ unsigned long tmp = inl(fifoaddr);
+ memcpy((unsigned char *)buf + count * 4, &tmp,
+ len & 0x03);
+ }
+ } else {
+ len = (len + 1) / 2;
+ insw(fifoaddr, buf, len);
}
-#else
- len = (len + 1) / 2;
- insw(r8a66597->reg + offset, buf, len);
-#endif
}
static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
@@ -578,33 +206,33 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
int len)
{
unsigned long fifoaddr = r8a66597->reg + offset;
-#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
unsigned long count;
unsigned char *pb;
int i;
- count = len / 4;
- outsl(fifoaddr, buf, count);
+ if (r8a66597->pdata->on_chip) {
+ 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;
- 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);
+ len = len / 2;
+ outsw(fifoaddr, buf, len);
+ if (unlikely(odd)) {
+ buf = &buf[len];
+ outb((unsigned char)*buf, fifoaddr);
}
}
-#else
- int odd = len & 0x0001;
-
- len = len / 2;
- outsw(fifoaddr, buf, len);
- if (unlikely(odd)) {
- buf = &buf[len];
- outb((unsigned char)*buf, fifoaddr);
- }
-#endif
}
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index cef3e1d9b92e..11af4cb8924e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1869,7 +1869,7 @@ config FB_W100
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
- depends on FB && SUPERH
+ depends on FB && SUPERH && HAVE_CLK
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 07f22b625632..3ad5157f9899 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -14,6 +14,7 @@
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
@@ -22,35 +23,8 @@
#include <asm/atomic.h>
#define PALETTE_NR 16
-
-struct sh_mobile_lcdc_priv;
-struct sh_mobile_lcdc_chan {
- struct sh_mobile_lcdc_priv *lcdc;
- unsigned long *reg_offs;
- unsigned long ldmt1r_value;
- unsigned long enabled; /* ME and SE in LDCNT2R */
- struct sh_mobile_lcdc_chan_cfg cfg;
- u32 pseudo_palette[PALETTE_NR];
- struct fb_info *info;
- dma_addr_t dma_handle;
- struct fb_deferred_io defio;
- struct scatterlist *sglist;
- unsigned long frame_end;
- wait_queue_head_t frame_end_wait;
-};
-
-struct sh_mobile_lcdc_priv {
- void __iomem *base;
- int irq;
-#ifdef CONFIG_HAVE_CLK
- atomic_t clk_usecnt;
- struct clk *dot_clk;
- struct clk *clk;
-#endif
- unsigned long lddckr;
- struct sh_mobile_lcdc_chan ch[2];
- int started;
-};
+#define SIDE_B_OFFSET 0x1000
+#define MIRROR_OFFSET 0x2000
/* shared registers */
#define _LDDCKR 0x410
@@ -59,17 +33,30 @@ struct sh_mobile_lcdc_priv {
#define _LDSR 0x46c
#define _LDCNT1R 0x470
#define _LDCNT2R 0x474
+#define _LDRCNTR 0x478
#define _LDDDSR 0x47c
#define _LDDWD0R 0x800
#define _LDDRDR 0x840
#define _LDDWAR 0x900
#define _LDDRAR 0x904
+/* shared registers and their order for context save/restore */
+static int lcdc_shared_regs[] = {
+ _LDDCKR,
+ _LDDCKSTPR,
+ _LDINTR,
+ _LDDDSR,
+ _LDCNT1R,
+ _LDCNT2R,
+};
+#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
+
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
- LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
+ LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
+ NR_CH_REGS };
-static unsigned long lcdc_offs_mainlcd[] = {
+static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
[LDDCKPAT1R] = 0x400,
[LDDCKPAT2R] = 0x404,
[LDMT1R] = 0x418,
@@ -87,7 +74,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
[LDPMR] = 0x460,
};
-static unsigned long lcdc_offs_sublcd[] = {
+static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
[LDDCKPAT1R] = 0x408,
[LDDCKPAT2R] = 0x40c,
[LDMT1R] = 0x600,
@@ -110,12 +97,80 @@ static unsigned long lcdc_offs_sublcd[] = {
#define DISPLAY_BEU 0x00000008
#define LCDC_ENABLE 0x00000001
#define LDINTR_FE 0x00000400
+#define LDINTR_VSE 0x00000200
+#define LDINTR_VEE 0x00000100
#define LDINTR_FS 0x00000004
+#define LDINTR_VSS 0x00000002
+#define LDINTR_VES 0x00000001
+#define LDRCNTR_SRS 0x00020000
+#define LDRCNTR_SRC 0x00010000
+#define LDRCNTR_MRS 0x00000002
+#define LDRCNTR_MRC 0x00000001
+
+struct sh_mobile_lcdc_priv;
+struct sh_mobile_lcdc_chan {
+ struct sh_mobile_lcdc_priv *lcdc;
+ unsigned long *reg_offs;
+ unsigned long ldmt1r_value;
+ unsigned long enabled; /* ME and SE in LDCNT2R */
+ struct sh_mobile_lcdc_chan_cfg cfg;
+ u32 pseudo_palette[PALETTE_NR];
+ unsigned long saved_ch_regs[NR_CH_REGS];
+ struct fb_info *info;
+ dma_addr_t dma_handle;
+ struct fb_deferred_io defio;
+ struct scatterlist *sglist;
+ unsigned long frame_end;
+ unsigned long pan_offset;
+ unsigned long new_pan_offset;
+ wait_queue_head_t frame_end_wait;
+};
+
+struct sh_mobile_lcdc_priv {
+ void __iomem *base;
+ int irq;
+ atomic_t hw_usecnt;
+ struct device *dev;
+ struct clk *dot_clk;
+ unsigned long lddckr;
+ struct sh_mobile_lcdc_chan ch[2];
+ unsigned long saved_shared_regs[NR_SHARED_REGS];
+ int started;
+};
+
+static bool banked(int reg_nr)
+{
+ switch (reg_nr) {
+ case LDMT1R:
+ case LDMT2R:
+ case LDMT3R:
+ case LDDFR:
+ case LDSM1R:
+ case LDSA1R:
+ case LDMLSR:
+ case LDHCNR:
+ case LDHSYNR:
+ case LDVLNR:
+ case LDVSYNR:
+ return true;
+ }
+ return false;
+}
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
int reg_nr, unsigned long data)
{
iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);
+ if (banked(reg_nr))
+ iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] +
+ SIDE_B_OFFSET);
+}
+
+static void lcdc_write_chan_mirror(struct sh_mobile_lcdc_chan *chan,
+ int reg_nr, unsigned long data)
+{
+ iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] +
+ MIRROR_OFFSET);
}
static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
@@ -156,6 +211,7 @@ static void lcdc_sys_write_index(void *handle, unsigned long data)
lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
}
static void lcdc_sys_write_data(void *handle, unsigned long data)
@@ -165,6 +221,7 @@ static void lcdc_sys_write_data(void *handle, unsigned long data)
lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
}
static unsigned long lcdc_sys_read_data(void *handle)
@@ -175,8 +232,9 @@ static unsigned long lcdc_sys_read_data(void *handle)
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
udelay(1);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
- return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff;
+ return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff;
}
struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
@@ -185,11 +243,10 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_read_data,
};
-#ifdef CONFIG_HAVE_CLK
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
{
- if (atomic_inc_and_test(&priv->clk_usecnt)) {
- clk_enable(priv->clk);
+ if (atomic_inc_and_test(&priv->hw_usecnt)) {
+ pm_runtime_get_sync(priv->dev);
if (priv->dot_clk)
clk_enable(priv->dot_clk);
}
@@ -197,16 +254,12 @@ static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{
- if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {
+ if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
if (priv->dot_clk)
clk_disable(priv->dot_clk);
- clk_disable(priv->clk);
+ pm_runtime_put(priv->dev);
}
}
-#else
-static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}
-static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}
-#endif
static int sh_mobile_lcdc_sginit(struct fb_info *info,
struct list_head *pagelist)
@@ -255,30 +308,52 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
struct sh_mobile_lcdc_priv *priv = data;
struct sh_mobile_lcdc_chan *ch;
unsigned long tmp;
+ unsigned long ldintr;
int is_sub;
int k;
/* acknowledge interrupt */
- tmp = lcdc_read(priv, _LDINTR);
- tmp &= 0xffffff00; /* mask in high 24 bits */
- tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
+ ldintr = tmp = lcdc_read(priv, _LDINTR);
+ /*
+ * disable further VSYNC End IRQs, preserve all other enabled IRQs,
+ * write 0 to bits 0-6 to ack all triggered IRQs.
+ */
+ tmp &= 0xffffff00 & ~LDINTR_VEE;
lcdc_write(priv, _LDINTR, tmp);
/* figure out if this interrupt is for main or sub lcd */
is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0;
- /* wake up channel and disable clocks*/
+ /* wake up channel and disable clocks */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
if (!ch->enabled)
continue;
- if (is_sub == lcdc_chan_is_sublcd(ch)) {
- ch->frame_end = 1;
- wake_up(&ch->frame_end_wait);
+ /* Frame Start */
+ if (ldintr & LDINTR_FS) {
+ if (is_sub == lcdc_chan_is_sublcd(ch)) {
+ ch->frame_end = 1;
+ wake_up(&ch->frame_end_wait);
- sh_mobile_lcdc_clk_off(priv);
+ sh_mobile_lcdc_clk_off(priv);
+ }
+ }
+
+ /* VSYNC End */
+ if (ldintr & LDINTR_VES) {
+ unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR);
+ /* Set the source address for the next refresh */
+ lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle +
+ ch->new_pan_offset);
+ if (lcdc_chan_is_sublcd(ch))
+ lcdc_write(ch->lcdc, _LDRCNTR,
+ ldrcntr ^ LDRCNTR_SRS);
+ else
+ lcdc_write(ch->lcdc, _LDRCNTR,
+ ldrcntr ^ LDRCNTR_MRS);
+ ch->pan_offset = ch->new_pan_offset;
}
}
@@ -520,7 +595,6 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_off)
board_cfg->display_off(board_cfg->board_data);
-
}
/* stop the lcdc */
@@ -579,9 +653,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
int clock_source,
struct sh_mobile_lcdc_priv *priv)
{
-#ifdef CONFIG_HAVE_CLK
- char clk_name[8];
-#endif
char *str;
int icksel;
@@ -595,25 +666,21 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
priv->lddckr = icksel << 16;
-#ifdef CONFIG_HAVE_CLK
- atomic_set(&priv->clk_usecnt, -1);
- snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
- priv->clk = clk_get(&pdev->dev, clk_name);
- if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
- return PTR_ERR(priv->clk);
- }
-
if (str) {
priv->dot_clk = clk_get(&pdev->dev, str);
if (IS_ERR(priv->dot_clk)) {
dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
- clk_put(priv->clk);
return PTR_ERR(priv->dot_clk);
}
}
-#endif
-
+ atomic_set(&priv->hw_usecnt, -1);
+
+ /* Runtime PM support involves two step for this driver:
+ * 1) Enable Runtime PM
+ * 2) Force Runtime PM Resume since hardware is accessed from probe()
+ */
+ pm_runtime_enable(priv->dev);
+ pm_runtime_resume(priv->dev);
return 0;
}
@@ -646,6 +713,9 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.accel = FB_ACCEL_NONE,
+ .xpanstep = 0,
+ .ypanstep = 1,
+ .ywrapstep = 0,
};
static void sh_mobile_lcdc_fillrect(struct fb_info *info,
@@ -669,13 +739,38 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
sh_mobile_lcdc_deferred_io_touch(info);
}
+static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct sh_mobile_lcdc_chan *ch = info->par;
+
+ if (info->var.xoffset == var->xoffset &&
+ info->var.yoffset == var->yoffset)
+ return 0; /* No change, do nothing */
+
+ ch->new_pan_offset = (var->yoffset * info->fix.line_length) +
+ (var->xoffset * (info->var.bits_per_pixel / 8));
+
+ if (ch->new_pan_offset != ch->pan_offset) {
+ unsigned long ldintr;
+ ldintr = lcdc_read(ch->lcdc, _LDINTR);
+ ldintr |= LDINTR_VEE;
+ lcdc_write(ch->lcdc, _LDINTR, ldintr);
+ sh_mobile_lcdc_deferred_io_touch(info);
+ }
+
+ return 0;
+}
+
static struct fb_ops sh_mobile_lcdc_ops = {
+ .owner = THIS_MODULE,
.fb_setcolreg = sh_mobile_lcdc_setcolreg,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_fillrect = sh_mobile_lcdc_fillrect,
.fb_copyarea = sh_mobile_lcdc_copyarea,
.fb_imageblit = sh_mobile_lcdc_imageblit,
+ .fb_pan_display = sh_mobile_fb_pan_display,
};
static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -731,9 +826,59 @@ static int sh_mobile_lcdc_resume(struct device *dev)
return sh_mobile_lcdc_start(platform_get_drvdata(pdev));
}
+static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
+ struct sh_mobile_lcdc_chan *ch;
+ int k, n;
+
+ /* save per-channel registers */
+ for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
+ ch = &p->ch[k];
+ if (!ch->enabled)
+ continue;
+ for (n = 0; n < NR_CH_REGS; n++)
+ ch->saved_ch_regs[n] = lcdc_read_chan(ch, n);
+ }
+
+ /* save shared registers */
+ for (n = 0; n < NR_SHARED_REGS; n++)
+ p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]);
+
+ /* turn off LCDC hardware */
+ lcdc_write(p, _LDCNT1R, 0);
+ return 0;
+}
+
+static int sh_mobile_lcdc_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
+ struct sh_mobile_lcdc_chan *ch;
+ int k, n;
+
+ /* restore per-channel registers */
+ for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
+ ch = &p->ch[k];
+ if (!ch->enabled)
+ continue;
+ for (n = 0; n < NR_CH_REGS; n++)
+ lcdc_write_chan(ch, n, ch->saved_ch_regs[n]);
+ }
+
+ /* restore shared registers */
+ for (n = 0; n < NR_SHARED_REGS; n++)
+ lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);
+
+ return 0;
+}
+
static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
.suspend = sh_mobile_lcdc_suspend,
.resume = sh_mobile_lcdc_resume,
+ .runtime_suspend = sh_mobile_lcdc_runtime_suspend,
+ .runtime_resume = sh_mobile_lcdc_runtime_resume,
};
static int sh_mobile_lcdc_remove(struct platform_device *pdev);
@@ -778,6 +923,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
}
priv->irq = i;
+ priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
pdata = pdev->dev.platform_data;
@@ -792,6 +938,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
init_waitqueue_head(&priv->ch[i].frame_end_wait);
+ priv->ch[j].pan_offset = 0;
+ priv->ch[j].new_pan_offset = 0;
switch (pdata->ch[i].chan) {
case LCDC_CHAN_MAINLCD:
@@ -834,7 +982,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
info = priv->ch[i].info;
info->fbops = &sh_mobile_lcdc_ops;
info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
- info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres;
+ info->var.yres = cfg->lcd_cfg.yres;
+ /* Default Y virtual resolution is 2x panel size */
+ info->var.yres_virtual = info->var.yres * 2;
info->var.width = cfg->lcd_size_cfg.width;
info->var.height = cfg->lcd_size_cfg.height;
info->var.activate = FB_ACTIVATE_NOW;
@@ -844,7 +994,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
info->fix = sh_mobile_lcdc_fix;
info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
- info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres;
+ info->fix.smem_len = info->fix.line_length *
+ info->var.yres_virtual;
buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
&priv->ch[i].dma_handle, GFP_KERNEL);
@@ -947,11 +1098,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
framebuffer_release(info);
}
-#ifdef CONFIG_HAVE_CLK
if (priv->dot_clk)
clk_put(priv->dot_clk);
- clk_put(priv->clk);
-#endif
+
+ pm_runtime_disable(priv->dev);
if (priv->base)
iounmap(priv->base);