// SPDX-License-Identifier: GPL-2.0
/*
* Fusb300 UDC (USB gadget)
*
* Copyright (C) 2010 Faraday Technology Corp.
*
* Author : Yuan-hsin Chen <yhchen@faraday-tech.com>
*/
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "fusb300_udc.h"
MODULE_DESCRIPTION("FUSB300 USB gadget driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
MODULE_ALIAS("platform:fusb300_udc");
#define DRIVER_VERSION "20 October 2010"
static const char udc_name[] = "fusb300_udc";
static const char * const fusb300_ep_name[] = {
"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "ep9",
"ep10", "ep11", "ep12", "ep13", "ep14", "ep15"
};
static void done(struct fusb300_ep *ep, struct fusb300_request *req,
int status);
static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset,
u32 value)
{
u32 reg = ioread32(fusb300->reg + offset);
reg |= value;
iowrite32(reg, fusb300->reg + offset);
}
static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset,
u32 value)
{
u32 reg = ioread32(fusb300->reg + offset);
reg &= ~value;
iowrite32(reg, fusb300->reg + offset);
}
static void fusb300_ep_setting(struct fusb300_ep *ep,
struct fusb300_ep_info info)
{
ep->epnum = info.epnum;
ep->type = info.type;
}
static int fusb300_ep_release(struct fusb300_ep *ep)
{
if (!ep->epnum)
return 0;
ep->epnum = 0;
ep->stall = 0;
ep->wedged = 0;
return 0;
}
static void fusb300_set_fifo_entry(struct fusb300 *fusb300,
u32 ep)
{
u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
val &= ~FUSB300_EPSET1_FIFOENTRY_MSK;
val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM);
iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
}
static void fusb300_set_start_entry(struct fusb300 *fusb300,
u8 ep)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM;
reg &= ~FUSB300_EPSET1_START_ENTRY_MSK ;
reg |= FUSB300_EPSET1_START_ENTRY(start_entry);
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) {
fusb300->fifo_entry_num = 0;
fusb300->addrofs = 0;
pr_err("fifo entry is over the maximum number!\n");
} else
fusb300->fifo_entry_num++;
}
/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */
static void fusb300_set_epaddrofs(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
reg &= ~FUSB300_EPSET2_ADDROFS_MSK;
reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs);
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM;
}
static void ep_fifo_setting(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
fusb300_set_fifo_entry(fusb300, info.epnum);
fusb300_set_start_entry(fusb300, info.epnum);
fusb300_set_epaddrofs(fusb300, info);
}
static void fusb300_set_eptype(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
reg &= ~FUSB300_EPSET1_TYPE_MSK;
reg |= FUSB300_EPSET1_TYPE(info.type);
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
}
static void fusb300_set_epdir(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
u32 reg;
if (!info.dir_in)
return;
reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
reg &= ~FUSB300_EPSET1_DIR_MSK;
reg |= FUSB300_EPSET1_DIRIN;
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
}
static void fusb300_set_ep_active(struct fusb300 *fusb300,
u8 ep)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
reg |= FUSB300_EPSET1_ACTEN;
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
}
static void fusb300_set_epmps(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
reg &= ~FUSB300_EPSET2_MPS_MSK;
reg |= FUSB300_EPSET2_MPS(info.maxpacket);
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
}
static void fusb300_set_interval(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
reg &= ~FUSB300_EPSET1_INTERVAL(0x7);
reg |= FUSB300_EPSET1_INTERVAL(info.interval);
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
}
static void fusb300_set_bwnum(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
reg &= ~FUSB300_EPSET1_BWNUM(0x3);
reg |= FUSB300_EPSET1_BWNUM(info.bw_num);
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
}
static void set_ep_reg(struct fusb300 *fusb300,
struct fusb300_ep_info info)
{
fusb300_set_eptype(fusb300, info);
fusb300_set_epdir(fusb300, info);
fusb300_set_epmps(fusb300, info);
if (info.interval)
fusb300_set_interval(fusb300, info);
if (info.bw_num)
fusb300_set_bwnum(fusb300, info);
fusb300_set_ep_active(fusb300, info.epnum);
}
static int config_ep(struct fusb300_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
struct fusb300 *fusb300 = ep->fusb300;
struct fusb300_ep_info info;
ep->ep.desc = desc;
info.interval = 0;
info.addrofs = 0;
info.bw_num = 0;
info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
info.maxpacket = usb_endpoint_maxp(desc);
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
if ((info.type == USB_ENDPOINT_XFER_INT) ||
(info.type == USB_ENDPOINT_XFER_ISOC)) {
info.interval = desc->bInterval;
if (info.type == USB_ENDPOINT_XFER_ISOC)
info.bw_num = usb_endpoint_maxp_mult(desc);
}
ep_fifo_setting(fusb300, info);
set_ep_reg(fusb300, info);
fusb300_ep_setting(ep, info);
fusb300->ep[info.epnum] = ep;
return 0;
}
static int fusb300_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct fusb300_ep *ep;
ep = container_of(_ep, struct fusb300_ep, ep);
if (ep->fusb300->reenum) {
ep->fusb300->fifo_entry_num = 0;
ep->fusb300->addrofs = 0;
ep->fusb300->reenum = 0;
}
return config_ep(ep, desc);
}
static int fusb300_disable(struct usb_ep *_ep)
{
struct fusb300_ep *ep;
struct fusb300_request *req;
unsigned long flags;
ep = container_of(_ep, struct fusb300_ep, ep);
BUG_ON(!ep);
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct fusb300_request, queue);
spin_lock_irqsave(&ep->fusb300->lock, flags);
done(ep, req, -ECONNRESET);
spin_unlock_irqrestore(&ep->fusb300->lock, flags);
}
return fusb300_ep_release(ep);
}
static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep,
gfp_t gfp_flags)
{
struct fusb300_request *req;
req = kzalloc(sizeof(struct fusb300_request), gfp_flags);
if (!req)
return NULL;
INIT_LIST_HEAD(&req->queue);
return &req->req;
}
static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
struct fusb300_request *req;
req = container_of(_req, struct fusb300_request, req);
kfree(req);
}
static int enable_fifo_int(struct fusb300_ep *ep)
{
struct fusb300 *fusb300 = ep->fusb300;
if (ep->epnum) {
fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0,
FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
} else {
pr_err("can't enable_fifo_int ep0\n");
return -EINVAL;
}
r
|