linux/drivers/usb/host/sl811-hcd.c

// SPDX-License-Identifier: GPL-2.0
/*
 * SL811HS HCD (Host Controller Driver) for USB.
 *
 * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
 * Copyright (C) 2004-2005 David Brownell
 *
 * Periodic scheduling is based on Roman's OHCI code
 * 	Copyright (C) 1999 Roman Weissgaerber
 *
 * The SL811HS controller handles host side USB (like the SL11H, but with
 * another register set and SOF generation) as well as peripheral side USB
 * (like the SL811S).  This driver version doesn't implement the Gadget API
 * for the peripheral role; or OTG (that'd need much external circuitry).
 *
 * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
 * document (providing significant pieces missing from that spec); plus
 * the SL811S spec if you want peripheral side info.
 */

/*
 * Status:  Passed basic stress testing, works with hubs, mice, keyboards,
 * and usb-storage.
 *
 * TODO:
 * - usb suspend/resume triggered by sl811
 * - various issues noted in the code
 * - performance work; use both register banks; ...
 * - use urb->iso_frame_desc[] with ISO transfers
 */

#undef	VERBOSE
#undef	PACKET_TRACE

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/usb/sl811.h>
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
#include <linux/prefetch.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>

#include "sl811.h"


MODULE_DESCRIPTION();
MODULE_LICENSE();
MODULE_ALIAS();

#define DRIVER_VERSION

/* for now, use only one transfer register bank */
#undef	USE_B

// #define	QUIRK2
#define QUIRK3

static const char hcd_name[] =;

/*-------------------------------------------------------------------------*/

static void port_power(struct sl811 *sl811, int is_on)
{}

/*-------------------------------------------------------------------------*/

/* This is a PIO-only HCD.  Queueing appends URBs to the endpoint's queue,
 * and may start I/O.  Endpoint queues are scanned during completion irq
 * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation.
 *
 * Using an external DMA engine to copy a packet at a time could work,
 * though setup/teardown costs may be too big to make it worthwhile.
 */

/* SETUP starts a new control request.  Devices are not allowed to
 * STALL or NAK these; they must cancel any pending control requests.
 */
static void setup_packet(
	struct sl811		*sl811,
	struct sl811h_ep	*ep,
	struct urb		*urb,
	u8			bank,
	u8			control
)
{}

/* STATUS finishes control requests, often after IN or OUT data packets */
static void status_packet(
	struct sl811		*sl811,
	struct sl811h_ep	*ep,
	struct urb		*urb,
	u8			bank,
	u8			control
)
{}

/* IN packets can be used with any type of endpoint. here we just
 * start the transfer, data from the peripheral may arrive later.
 * urb->iso_frame_desc is currently ignored here...
 */
static void in_packet(
	struct sl811		*sl811,
	struct sl811h_ep	*ep,
	struct urb		*urb,
	u8			bank,
	u8			control
)
{}

/* OUT packets can be used with any type of endpoint.
 * urb->iso_frame_desc is currently ignored here...
 */
static void out_packet(
	struct sl811		*sl811,
	struct sl811h_ep	*ep,
	struct urb		*urb,
	u8			bank,
	u8			control
)
{}

/*-------------------------------------------------------------------------*/

/* caller updates on-chip enables later */

static inline void sofirq_on(struct sl811 *sl811)
{}

static inline void sofirq_off(struct sl811 *sl811)
{}

/*-------------------------------------------------------------------------*/

/* pick the next endpoint for a transaction, and issue it.
 * frames start with periodic transfers (after whatever is pending
 * from the previous frame), and the rest of the time is async
 * transfers, scheduled round-robin.
 */
static struct sl811h_ep	*start(struct sl811 *sl811, u8 bank)
{}

#define MIN_JIFFIES

static inline void start_transfer(struct sl811 *sl811)
{}

static void finish_request(
	struct sl811		*sl811,
	struct sl811h_ep	*ep,
	struct urb		*urb,
	int			status
) __releases(sl811->lock) __acquires(sl811->lock)
{}

static void
done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank)
{}

#ifdef QUIRK2
static inline u8 checkdone(struct sl811 *sl811)
{
	u8	ctl;
	u8	irqstat = 0;

	if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {
		ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));
		if (ctl & SL11H_HCTLMASK_ARM)
			sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
		dev_dbg(sl811_to_hcd(sl811)->self.controller,
			"%s DONE_A: ctrl %02x sts %02x\n",
			(ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
			ctl,
			sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
		irqstat |= SL11H_INTMASK_DONE_A;
	}
#ifdef	USE_B
	if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {
		ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));
		if (ctl & SL11H_HCTLMASK_ARM)
			sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
		dev_dbg(sl811_to_hcd(sl811)->self.controller,
			"%s DONE_B: ctrl %02x sts %02x\n",
			(ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
			ctl,
			sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
		irqstat |= SL11H_INTMASK_DONE_A;
	}
#endif
	return irqstat;
}
#endif

static irqreturn_t sl811h_irq(struct usb_hcd *hcd)
{}

/*-------------------------------------------------------------------------*/

/* usb 1.1 says max 90% of a frame is available for periodic transfers.
 * this driver doesn't promise that much since it's got to handle an
 * IRQ per packet; irq handling latencies also use up that time.
 *
 * NOTE:  the periodic schedule is a sparse tree, with the load for
 * each branch minimized.  see fig 3.5 in the OHCI spec for example.
 */
#define MAX_PERIODIC_LOAD

static int balance(struct sl811 *sl811, u16 period, u16 load)
{}

/*-------------------------------------------------------------------------*/

static int sl811h_urb_enqueue(
	struct usb_hcd		*hcd,
	struct urb		*urb,
	gfp_t			mem_flags
) {}

static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{}

static void
sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
{}

static int
sl811h_get_frame(struct usb_hcd *hcd)
{}


/*-------------------------------------------------------------------------*/

/* the virtual root hub timer IRQ checks for hub status */
static int
sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
{}

static void
sl811h_hub_descriptor (
	struct sl811			*sl811,
	struct usb_hub_descriptor	*desc
) {}

static void
sl811h_timer(struct timer_list *t)
{}

static int
sl811h_hub_control(
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
) {}

#ifdef	CONFIG_PM

static int
sl811h_bus_suspend(struct usb_hcd *hcd)
{}

static int
sl811h_bus_resume(struct usb_hcd *hcd)
{}

#else

#define sl811h_bus_suspend
#define sl811h_bus_resume

#endif


/*-------------------------------------------------------------------------*/

static void dump_irq(struct seq_file *s, char *label, u8 mask)
{}

static int sl811h_debug_show(struct seq_file *s, void *unused)
{}
DEFINE_SHOW_ATTRIBUTE();

/* expect just one sl811 per system */
static void create_debug_file(struct sl811 *sl811)
{}

static void remove_debug_file(struct sl811 *sl811)
{}

/*-------------------------------------------------------------------------*/

static void
sl811h_stop(struct usb_hcd *hcd)
{}

static int
sl811h_start(struct usb_hcd *hcd)
{}

/*-------------------------------------------------------------------------*/

static const struct hc_driver sl811h_hc_driver =;

/*-------------------------------------------------------------------------*/

static void
sl811h_remove(struct platform_device *dev)
{}

static int
sl811h_probe(struct platform_device *dev)
{}

#ifdef	CONFIG_PM

/* for this device there's no useful distinction between the controller
 * and its root hub.
 */

static int
sl811h_suspend(struct platform_device *dev, pm_message_t state)
{}

static int
sl811h_resume(struct platform_device *dev)
{}

#else

#define sl811h_suspend
#define sl811h_resume

#endif


/* this driver is exported so sl811_cs can depend on it */
struct platform_driver sl811h_driver =;
EXPORT_SYMBOL();

module_platform_driver();