Commit f1407d5c authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Greg Kroah-Hartman

usb: renesas_usbhs: Add Renesas USBHS common code

Renesas SuperH has USBHS IP which can switch Host / Function.
This driver is designed so that Host / Function may dynamically change.
This patch add usb/renesas_usbhs and common code for SuperH USBHS.
Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent a6360dd3
...@@ -67,6 +67,7 @@ obj-$(CONFIG_UWB) += uwb/ ...@@ -67,6 +67,7 @@ obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/
obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/
obj-$(CONFIG_USB_RENESAS_USBHS) += usb/renesas_usbhs/
obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_SERIO) += input/serio/
......
...@@ -115,6 +115,8 @@ source "drivers/usb/host/Kconfig" ...@@ -115,6 +115,8 @@ source "drivers/usb/host/Kconfig"
source "drivers/usb/musb/Kconfig" source "drivers/usb/musb/Kconfig"
source "drivers/usb/renesas_usbhs/Kconfig"
source "drivers/usb/class/Kconfig" source "drivers/usb/class/Kconfig"
source "drivers/usb/storage/Kconfig" source "drivers/usb/storage/Kconfig"
......
#
# Renesas USB Controller Drivers
#
config USB_RENESAS_USBHS
tristate 'Renesas USBHS controller'
default n
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
It has nine or more configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "renesas_usbhs" and force all
gadget drivers to also be dynamically linked.
#
# for Renesas USB
#
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
renesas_usbhs-y := common.o mod.o pipe.o
/*
* Renesas USB driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* 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/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "./common.h"
/*
* platform call back
*
* renesas usb support platform callback function.
* Below macro call it.
* if platform doesn't have callback, it return 0 (no error)
*/
#define usbhs_platform_call(priv, func, args...)\
(!(priv) ? -ENODEV : \
!((priv)->pfunc->func) ? 0 : \
(priv)->pfunc->func(args))
/*
* common functions
*/
u16 usbhs_read(struct usbhs_priv *priv, u32 reg)
{
return ioread16(priv->base + reg);
}
void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data)
{
iowrite16(data, priv->base + reg);
}
void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data)
{
u16 val = usbhs_read(priv, reg);
val &= ~mask;
val |= data & mask;
usbhs_write(priv, reg, val);
}
/*
* syscfg functions
*/
void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable)
{
usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0);
}
void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable)
{
usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0);
}
void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable)
{
usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0);
}
void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
{
u16 mask = DCFM | DRPD | DPRPU;
u16 val = DCFM | DRPD;
/*
* if enable
*
* - select Host mode
* - D+ Line/D- Line Pull-down
*/
usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
}
void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
{
u16 mask = DCFM | DRPD | DPRPU;
u16 val = DPRPU;
/*
* if enable
*
* - select Function mode
* - D+ Line Pull-up
*/
usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
}
/*
* frame functions
*/
int usbhs_frame_get_num(struct usbhs_priv *priv)
{
return usbhs_read(priv, FRMNUM) & FRNM_MASK;
}
/*
* local functions
*/
static struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev)
{
return dev_get_drvdata(&pdev->dev);
}
static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable)
{
int wait = usbhs_get_dparam(priv, buswait_bwait);
u16 data = 0;
if (enable) {
/* set bus wait if platform have */
if (wait)
usbhs_bset(priv, BUSWAIT, 0x000F, wait);
}
usbhs_write(priv, DVSTCTR, data);
}
/*
* platform default param
*/
static u32 usbhsc_default_pipe_type[] = {
USB_ENDPOINT_XFER_CONTROL,
USB_ENDPOINT_XFER_ISOC,
USB_ENDPOINT_XFER_ISOC,
USB_ENDPOINT_XFER_BULK,
USB_ENDPOINT_XFER_BULK,
USB_ENDPOINT_XFER_BULK,
USB_ENDPOINT_XFER_INT,
USB_ENDPOINT_XFER_INT,
USB_ENDPOINT_XFER_INT,
USB_ENDPOINT_XFER_INT,
};
/*
* driver callback functions
*/
static void usbhsc_notify_hotplug(struct work_struct *work)
{
struct usbhs_priv *priv = container_of(work,
struct usbhs_priv,
notify_hotplug_work);
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
int id;
int enable;
int ret;
/*
* get vbus status from platform
*/
enable = usbhs_platform_call(priv, get_vbus, pdev);
/*
* get id from platform
*/
id = usbhs_platform_call(priv, get_id, pdev);
if (enable && !mod) {
ret = usbhs_mod_change(priv, id);
if (ret < 0)
return;
dev_dbg(&pdev->dev, "%s enable\n", __func__);
/* enable PM */
pm_runtime_get_sync(&pdev->dev);
/* USB on */
usbhs_sys_clock_ctrl(priv, enable);
usbhsc_bus_ctrl(priv, enable);
/* module start */
usbhs_mod_call(priv, start, priv);
} else if (!enable && mod) {
dev_dbg(&pdev->dev, "%s disable\n", __func__);
/* module stop */
usbhs_mod_call(priv, stop, priv);
/* USB off */
usbhsc_bus_ctrl(priv, enable);
usbhs_sys_clock_ctrl(priv, enable);
/* disable PM */
pm_runtime_put_sync(&pdev->dev);
usbhs_mod_change(priv, -1);
/* reset phy for next connection */
usbhs_platform_call(priv, phy_reset, pdev);
}
}
static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
{
struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
/*
* This functions will be called in interrupt.
* To make sure safety context,
* use workqueue for usbhs_notify_hotplug
*/
schedule_work(&priv->notify_hotplug_work);
return 0;
}
/*
* platform functions
*/
static int __devinit usbhs_probe(struct platform_device *pdev)
{
struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
struct renesas_usbhs_driver_callback *dfunc;
struct usbhs_priv *priv;
struct resource *res;
unsigned int irq;
int ret;
/* check platform information */
if (!info ||
!info->platform_callback.get_id ||
!info->platform_callback.get_vbus) {
dev_err(&pdev->dev, "no platform information\n");
return -EINVAL;
}
/* platform data */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0) {
dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
return -ENODEV;
}
/* usb private data */
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "Could not allocate priv\n");
return -ENOMEM;
}
priv->base = ioremap_nocache(res->start, resource_size(res));
if (!priv->base) {
dev_err(&pdev->dev, "ioremap error.\n");
ret = -ENOMEM;
goto probe_end_kfree;
}
/*
* care platform info
*/
priv->pfunc = &info->platform_callback;
priv->dparam = &info->driver_param;
/* set driver callback functions for platform */
dfunc = &info->driver_callback;
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
/* set default param if platform doesn't have */
if (!priv->dparam->pipe_type) {
priv->dparam->pipe_type = usbhsc_default_pipe_type;
priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
}
/*
* priv settings
*/
priv->irq = irq;
priv->pdev = pdev;
INIT_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
spin_lock_init(usbhs_priv_to_lock(priv));
/* call pipe and module init */
ret = usbhs_pipe_probe(priv);
if (ret < 0)
goto probe_end_mod_exit;
ret = usbhs_mod_probe(priv);
if (ret < 0)
goto probe_end_iounmap;
/* dev_set_drvdata should be called after usbhs_mod_init */
dev_set_drvdata(&pdev->dev, priv);
/*
* deviece reset here because
* USB device might be used in boot loader.
*/
usbhs_sys_clock_ctrl(priv, 0);
/*
* platform call
*
* USB phy setup might depend on CPU/Board.
* If platform has its callback functions,
* call it here.
*/
ret = usbhs_platform_call(priv, hardware_init, pdev);
if (ret < 0) {
dev_err(&pdev->dev, "platform prove failed.\n");
goto probe_end_pipe_exit;
}
/* reset phy for connection */
usbhs_platform_call(priv, phy_reset, pdev);
/*
* manual call notify_hotplug for cold plug
*/
pm_runtime_enable(&pdev->dev);
ret = usbhsc_drvcllbck_notify_hotplug(pdev);
if (ret < 0)
goto probe_end_call_remove;
dev_info(&pdev->dev, "probed\n");
return ret;
probe_end_call_remove:
usbhs_platform_call(priv, hardware_exit, pdev);
probe_end_pipe_exit:
usbhs_pipe_remove(priv);
probe_end_mod_exit:
usbhs_mod_remove(priv);
probe_end_iounmap:
iounmap(priv->base);
probe_end_kfree:
kfree(priv);
dev_info(&pdev->dev, "probe failed\n");
return ret;
}
static int __devexit usbhs_remove(struct platform_device *pdev)
{
struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
dev_dbg(&pdev->dev, "usb remove\n");
pm_runtime_disable(&pdev->dev);
usbhsc_bus_ctrl(priv, 0);
usbhs_platform_call(priv, hardware_exit, pdev);
usbhs_pipe_remove(priv);
usbhs_mod_remove(priv);
iounmap(priv->base);
kfree(priv);
return 0;
}
static struct platform_driver renesas_usbhs_driver = {
.driver = {
.name = "renesas_usbhs",
},
.probe = usbhs_probe,
.remove = __devexit_p(usbhs_remove),
};
static int __init usbhs_init(void)
{
return platform_driver_register(&renesas_usbhs_driver);
}
static void __exit usbhs_exit(void)
{
platform_driver_unregister(&renesas_usbhs_driver);
}
module_init(usbhs_init);
module_exit(usbhs_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Renesas USB driver");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
/*
* Renesas USB driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* 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 RENESAS_USB_DRIVER_H
#define RENESAS_USB_DRIVER_H
#include <linux/platform_device.h>
#include <linux/usb/renesas_usbhs.h>
struct usbhs_priv;
#include "./mod.h"
#include "./pipe.h"
/*
*
* register define
*
*/
#define SYSCFG 0x0000
#define BUSWAIT 0x0002
#define DVSTCTR 0x0008
#define CFIFO 0x0014
#define CFIFOSEL 0x0020
#define CFIFOCTR 0x0022
#define INTENB0 0x0030
#define INTENB1 0x0032
#define BRDYENB 0x0036
#define NRDYENB 0x0038
#define BEMPENB 0x003A
#define INTSTS0 0x0040
#define INTSTS1 0x0042
#define BRDYSTS 0x0046
#define NRDYSTS 0x0048
#define BEMPSTS 0x004A
#define FRMNUM 0x004C
#define USBREQ 0x0054 /* USB request type register */
#define USBVAL 0x0056 /* USB request value register */
#define USBINDX 0x0058 /* USB request index register */
#define USBLENG 0x005A /* USB request length register */
#define DCPCFG 0x005C
#define DCPMAXP 0x005E
#define DCPCTR 0x0060
#define PIPESEL 0x0064
#define PIPECFG 0x0068
#define PIPEBUF 0x006A
#define PIPEMAXP 0x006C
#define PIPEPERI 0x006E
#define PIPEnCTR 0x0070
/* SYSCFG */
#define SCKE (1 << 10) /* USB Module Clock Enable */
#define HSE (1 << 7) /* High-Speed Operation Enable */
#define DCFM (1 << 6) /* Controller Function Select */
#define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */
#define DPRPU (1 << 4) /* D+ Line Resistance Control */
#define USBE (1 << 0) /* USB Module Operation Enable */
/* DVSTCTR */
#define EXTLP (1 << 10) /* Controls the EXTLP pin output state */
#define PWEN (1 << 9) /* Controls the PWEN pin output state */
#define RHST (0x7) /* Reset Handshake */
#define RHST_LOW_SPEED 1 /* Low-speed connection */
#define RHST_FULL_SPEED 2 /* Full-speed connection */
#define RHST_HIGH_SPEED 3 /* High-speed connection */
/* CFIFOSEL */
#define MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */
/* CFIFOCTR */
#define BVAL (1 << 15) /* Buffer Memory Enable Flag */
#define BCLR (1 << 14) /* CPU buffer clear */
#define FRDY (1 << 13) /* FIFO Port Ready */
#define DTLN_MASK (0x0FFF) /* Receive Data Length */
/* INTENB0 */
#define VBSE (1 << 15) /* Enable IRQ VBUS_0 and VBUSIN_0 */
#define RSME (1 << 14) /* Enable IRQ Resume */
#define SOFE (1 << 13) /* Enable IRQ Frame Number Update */
#define DVSE (1 << 12) /* Enable IRQ Device State Transition */
#define CTRE (1 << 11) /* Enable IRQ Control Stage Transition */
#define BEMPE (1 << 10) /* Enable IRQ Buffer Empty */
#define NRDYE (1 << 9) /* Enable IRQ Buffer Not Ready Response */
#define BRDYE (1 << 8) /* Enable IRQ Buffer Ready */
/* INTENB1 */
#define BCHGE (1 << 14) /* USB Bus Change Interrupt Enable */
#define DTCHE (1 << 12) /* Disconnection Detect Interrupt Enable */
#define ATTCHE (1 << 11) /* Connection Detect Interrupt Enable */
#define EOFERRE (1 << 6) /* EOF Error Detect Interrupt Enable */
#define SIGNE (1 << 5) /* Setup Transaction Error Interrupt Enable */
#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
/* INTSTS0 */
#define DVST (1 << 12) /* Device State Transition Interrupt Status */
#define CTRT (1 << 11) /* Control Stage Interrupt Status */
#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
#define BRDY (1 << 8) /* Buffer Ready Interrupt Status */
#define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */
#define VALID (1 << 3) /* USB Request Receive */
#define DVSQ_MASK (0x3 << 4) /* Device State */
#define POWER_STATE (0 << 4)
#define DEFAULT_STATE (1 << 4)
#define ADDRESS_STATE (2 << 4)
#define CONFIGURATION_STATE (3 << 4)
#define CTSQ_MASK (0x7) /* Control Transfer Stage */
#define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */
#define READ_DATA_STAGE 1 /* Control read data stage */
#define READ_STATUS_STAGE 2 /* Control read status stage */
#define WRITE_DATA_STAGE 3 /* Control write data stage */
#define WRITE_STATUS_STAGE 4 /* Control write status stage */
#define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */
#define SEQUENCE_ERROR 6 /* Control transfer sequence error */
/* PIPECFG */
/* DCPCFG */
#define TYPE_NONE (0 << 14) /* Transfer Type */
#define TYPE_BULK (1 << 14)
#define TYPE_INT (2 << 14)
#define TYPE_ISO (3 << 14)
#define DBLB (1 << 9) /* Double Buffer Mode */
#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */
#define DIR_OUT (1 << 4) /* Transfer Direction */
/* PIPEMAXP */
/* DCPMAXP */
#define DEVSEL_MASK (0xF << 12) /* Device Select */
#define DCP_MAXP_MASK (0x7F)
#define PIPE_MAXP_MASK (0x7FF)
/* PIPEBUF */
#define BUFSIZE_SHIFT 10
#define BUFSIZE_MASK (0x1F << BUFSIZE_SHIFT)
#define BUFNMB_MASK (0xFF)
/* PIPEnCTR */
/* DCPCTR */
#define BSTS (1 << 15) /* Buffer Status */
#define CSSTS (1 << 12) /* CSSTS Status */
#define SQCLR (1 << 8) /* Toggle Bit Clear */
#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
#define PBUSY (1 << 5) /* Pipe Busy */
#define PID_MASK (0x3) /* Response PID */
#define PID_NAK 0
#define PID_BUF 1
#define PID_STALL10 2
#define PID_STALL11 3
#define CCPL (1 << 2) /* Control Transfer End Enable */
/* FRMNUM */
#define FRNM_MASK (0x7FF)
/*
* struct
*/
struct usbhs_priv {
void __iomem *base;
unsigned int irq;
struct renesas_usbhs_platform_callback *pfunc;
struct renesas_usbhs_driver_param *dparam;
struct work_struct notify_hotplug_work;
struct platform_device *pdev;
spinlock_t lock;
/*
* module control
*/
struct usbhs_mod_info mod_info;
/*
* pipe control
*/
struct usbhs_pipe_info pipe_info;
};
/*
* common
*/
u16 usbhs_read(struct usbhs_priv *priv, u32 reg);
void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data);
void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data);
/*
* sysconfig
*/
void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable);
void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable);
void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable);
void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
/*
* frame
*/
int usbhs_frame_get_num(struct usbhs_priv *priv);
/*
* data
*/
#define usbhs_get_dparam(priv, param) (priv->dparam->param)
#define usbhs_priv_to_pdev(priv) (priv->pdev)
#define usbhs_priv_to_dev(priv) (&priv->pdev->dev)
#define usbhs_priv_to_lock(priv) (&priv->lock)
#endif /* RENESAS_USB_DRIVER_H */
/*
* Renesas USB driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* 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/interrupt.h>
#include "./common.h"
#include "./mod.h"
#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
/*
* host / gadget functions
*
* renesas_usbhs host/gadget can register itself by below functions.
* these functions are called when probe
*
*/
void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id)
{
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
info->mod[id] = mod;
mod->priv = priv;
}
struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
{
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
struct usbhs_mod *ret = NULL;
switch (id) {
case USBHS_HOST:
case USBHS_GADGET:
ret = info->mod[id];
break;
}
return ret;
}
int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod)
{
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
if (!mod)
return -EINVAL;
return info->mod[USBHS_HOST] == mod;
}
struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv)
{
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
return info->curt;
}
int usbhs_mod_change(struct usbhs_priv *priv, int id)
{
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
struct usbhs_mod *mod = NULL;
int ret = 0;
/* id < 0 mean no current */
switch (id) {
case USBHS_HOST:
case USBHS_GADGET:
mod = info->mod[id];
break;
default:
ret = -EINVAL;
}
info->curt = mod;
return ret;
}
static irqreturn_t usbhs_interrupt(int irq, void *data);
int usbhs_mod_probe(struct usbhs_priv *priv)
{
struct device *dev = usbhs_priv_to_dev(priv);
int ret;
/* irq settings */
ret = request_irq(priv->irq, usbhs_interrupt,
IRQF_DISABLED, dev_name(dev), priv);
if (ret)
dev_err(dev, "irq request err\n");
return ret;
}
void usbhs_mod_remove(struct usbhs_priv *priv)
{
free_irq(priv->irq, priv);
}
/*
* status functions
*/
int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state)
{
switch (irq_state->dvstctr & RHST) {
case RHST_LOW_SPEED:
return USB_SPEED_LOW;
case RHST_FULL_SPEED:
return USB_SPEED_FULL;
case RHST_HIGH_SPEED:
return USB_SPEED_HIGH;
}
return USB_SPEED_UNKNOWN;
}
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
{
int state = irq_state->intsts0 & DVSQ_MASK;
switch (state) {
case POWER_STATE:
case DEFAULT_STATE:
case ADDRESS_STATE:
case CONFIGURATION_STATE:
return state;
}
return -EIO;
}
int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
{
/*
* return value
*
* IDLE_SETUP_STAGE
* READ_DATA_STAGE
* READ_STATUS_STAGE
* WRITE_DATA_STAGE
* WRITE_STATUS_STAGE
* NODATA_STATUS_STAGE
* SEQUENCE_ERROR
*/
return (int)irq_state->intsts0 & CTSQ_MASK;
}
static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
struct usbhs_irq_state *state)
{
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
state->intsts0 = usbhs_read(priv, INTSTS0);
state->intsts1 = usbhs_read(priv, INTSTS1);
state->brdysts = usbhs_read(priv, BRDYSTS);
state->nrdysts = usbhs_read(priv, NRDYSTS);
state->bempsts = usbhs_read(priv, BEMPSTS);
state->dvstctr = usbhs_read(priv, DVSTCTR);
/* mask */
state->bempsts &= mod->irq_bempsts;
state->brdysts &= mod->irq_brdysts;
}
/*
* interrupt
*/
#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
static irqreturn_t usbhs_interrupt(int irq, void *data)
{
struct usbhs_priv *priv = data;
struct usbhs_irq_state irq_state;
usbhs_status_get_each_irq(priv, &irq_state);
/*
* clear interrupt
*
* The hardware is _very_ picky to clear interrupt bit.
* Especially INTSTS0_MAGIC, INTSTS1_MAGIC value.
*
* see
* "Operation"
* - "Control Transfer (DCP)"
* - Function :: VALID bit should 0
*/
usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
usbhs_write(priv, BRDYSTS, 0);
usbhs_write(priv, NRDYSTS, 0);
usbhs_write(priv, BEMPSTS, 0);
/*
* call irq callback functions
* see also
* usbhs_irq_setting_update
*/
if (irq_state.intsts0 & DVST)
usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
if (irq_state.intsts0 & CTRT)
usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state);
if (irq_state.intsts0 & BEMP)
usbhs_mod_call(priv, irq_empty, priv, &irq_state);
if (irq_state.intsts0 & BRDY)
usbhs_mod_call(priv, irq_ready, priv, &irq_state);
return IRQ_HANDLED;
}
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
{
u16 intenb0 = 0;
usbhs_write(priv, INTENB0, 0);
usbhs_write(priv, BEMPENB, 0);
usbhs_write(priv, BRDYENB, 0);
/*
* see also
* usbhs_interrupt
*/
/*
* it don't enable DVSE (intenb0) here
* but "mod->irq_dev_state" will be called.
*/
if (mod->irq_ctrl_stage)
intenb0 |= CTRE;
if (mod->irq_empty && mod->irq_bempsts) {
usbhs_write(priv, BEMPENB, mod->irq_bempsts);
intenb0 |= BEMPE;
}
if (mod->irq_ready && mod->irq_brdysts) {
usbhs_write(priv, BRDYENB, mod->irq_brdysts);
intenb0 |= BRDYE;
}
usbhs_write(priv, INTENB0, intenb0);
}
/*
* Renesas USB driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* 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 RENESAS_USB_MOD_H
#define RENESAS_USB_MOD_H
#include <linux/spinlock.h>
#include <linux/usb/renesas_usbhs.h>
#include "./common.h"
/*
* struct
*/
struct usbhs_irq_state {
u16 intsts0;
u16 intsts1;
u16 brdysts;
u16 nrdysts;
u16 bempsts;
u16 dvstctr;
};
struct usbhs_mod {
char *name;
/*
* entry point from common.c
*/
int (*start)(struct usbhs_priv *priv);
int (*stop)(struct usbhs_priv *priv);
/* INTSTS0 :: DVST (DVSQ) */
int (*irq_dev_state)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
/* INTSTS0 :: CTRT (CTSQ) */
int (*irq_ctrl_stage)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
/* INTSTS0 :: BEMP */
/* BEMPSTS */
int (*irq_empty)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
u16 irq_bempsts;
/* INTSTS0 :: BRDY */
/* BRDYSTS */
int (*irq_ready)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
u16 irq_brdysts;
struct usbhs_priv *priv;
};
struct usbhs_mod_info {
struct usbhs_mod *mod[USBHS_MAX];
struct usbhs_mod *curt; /* current mod */
};
/*
* for host/gadget module
*/
struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id);
struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv);
void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id);
int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod);
int usbhs_mod_change(struct usbhs_priv *priv, int id);
int usbhs_mod_probe(struct usbhs_priv *priv);
void usbhs_mod_remove(struct usbhs_priv *priv);
/*
* status functions
*/
int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state);
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state);
int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state);
/*
* callback functions
*/
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
#define usbhs_mod_call(priv, func, param...) \
({ \
struct usbhs_mod *mod; \
mod = usbhs_mod_get_current(priv); \
!mod ? -ENODEV : \
!mod->func ? 0 : \
mod->func(param); \
})
#endif /* RENESAS_USB_MOD_H */
This diff is collapsed.
/*
* Renesas USB driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* 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 RENESAS_USB_PIPE_H
#define RENESAS_USB_PIPE_H
#include "./common.h"
/*
* struct
*/
struct usbhs_pipe {
u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */
struct usbhs_priv *priv;
u32 flags;
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
void *mod_private;
};
struct usbhs_pipe_info {
struct usbhs_pipe *pipe;
int size; /* array size of "pipe" */
int bufnmb_last; /* FIXME : driver needs good allocator */
};
/*
* pipe list
*/
#define __usbhs_for_each_pipe(start, pos, info, i) \
for (i = start, pos = (info)->pipe; \
i < (info)->size; \
i++, pos = (info)->pipe + i)
#define usbhs_for_each_pipe(pos, priv, i) \
__usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i)
#define usbhs_for_each_pipe_with_dcp(pos, priv, i) \
__usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i)
/*
* pipe module probe / remove
*/
int usbhs_pipe_probe(struct usbhs_priv *priv);
void usbhs_pipe_remove(struct usbhs_priv *priv);
/*
* cfifo
*/
int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len);
int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len);
int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe);
int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
void usbhs_fifo_enable(struct usbhs_pipe *pipe);
void usbhs_fifo_disable(struct usbhs_pipe *pipe);
void usbhs_fifo_stall(struct usbhs_pipe *pipe);
void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe);
/*
* usb request
*/
void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
/*
* pipe control
*/
struct usbhs_pipe
*usbhs_pipe_malloc(struct usbhs_priv *priv,
const struct usb_endpoint_descriptor *desc);
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
void usbhs_pipe_init(struct usbhs_priv *priv);
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
#define usbhs_pipe_number(p) (((u32)(p) - (u32)(p)->priv->pipe_info.pipe) / \
sizeof(struct usbhs_pipe))
/*
* dcp control
*/
struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
#endif /* RENESAS_USB_PIPE_H */
/*
* Renesas USB
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* 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 RENESAS_USB_H
#define RENESAS_USB_H
#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
/*
* module type
*
* it will be return value from get_id
*/
enum {
USBHS_HOST = 0,
USBHS_GADGET,
USBHS_MAX,
};
/*
* callback functions table for driver
*
* These functions are called from platform for driver.
* Callback function's pointer will be set before
* renesas_usbhs_platform_callback :: hardware_init was called
*/
struct renesas_usbhs_driver_callback {
int (*notify_hotplug)(struct platform_device *pdev);
};
/*
* callback functions for platform
*
* These functions are called from driver for platform
*/
struct renesas_usbhs_platform_callback {
/*
* option:
*
* Hardware init function for platform.
* it is called when driver was probed.
*/
int (*hardware_init)(struct platform_device *pdev);
/*
* option:
*
* Hardware exit function for platform.
* it is called when driver was removed
*/
void (*hardware_exit)(struct platform_device *pdev);
/*
* option:
*
* Phy reset for platform
*/
void (*phy_reset)(struct platform_device *pdev);
/*
* get USB ID function
* - USBHS_HOST
* - USBHS_GADGET
*/
int (*get_id)(struct platform_device *pdev);
/*
* get VBUS status function.
*/
int (*get_vbus)(struct platform_device *pdev);
};
/*
* parameters for renesas usbhs
*
* some register needs USB chip specific parameters.
* This struct show it to driver
*/
struct renesas_usbhs_driver_param {
/*
* pipe settings
*/
u32 *pipe_type; /* array of USB_ENDPOINT_XFER_xxx (from ep0) */
int pipe_size; /* pipe_type array size */
/*
* option:
*
* for BUSWAIT :: BWAIT
* */
int buswait_bwait;
};
/*
* option:
*
* platform information for renesas_usbhs driver.
*/
struct renesas_usbhs_platform_info {
/*
* option:
*
* platform set these functions before
* call platform_add_devices if needed
*/
struct renesas_usbhs_platform_callback platform_callback;
/*
* driver set these callback functions pointer.
* platform can use it on callback functions
*/
struct renesas_usbhs_driver_callback driver_callback;
/*
* option:
*
* driver use these param for some register
*/
struct renesas_usbhs_driver_param driver_param;
};
/*
* macro for platform
*/
#define renesas_usbhs_get_info(pdev)\
((struct renesas_usbhs_platform_info *)(pdev)->dev.platform_data)
#define renesas_usbhs_call_notify_hotplug(pdev) \
({ \
struct renesas_usbhs_driver_callback *dc; \
dc = &(renesas_usbhs_get_info(pdev)->driver_callback); \
if (dc) \
dc->notify_hotplug(pdev); \
})
#endif /* RENESAS_USB_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment