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

// SPDX-License-Identifier: GPL-2.0
/*
 * MAX3421 Host Controller driver for USB.
 *
 * Author: David Mosberger-Tang <[email protected]>
 *
 * (C) Copyright 2014 David Mosberger-Tang <[email protected]>
 *
 * MAX3421 is a chip implementing a USB 2.0 Full-/Low-Speed host
 * controller on a SPI bus.
 *
 * Based on:
 *	o MAX3421E datasheet
 *		https://datasheets.maximintegrated.com/en/ds/MAX3421E.pdf
 *	o MAX3421E Programming Guide
 *		https://www.hdl.co.jp/ftpdata/utl-001/AN3785.pdf
 *	o gadget/dummy_hcd.c
 *		For USB HCD implementation.
 *	o Arduino MAX3421 driver
 *	     https://github.com/felis/USB_Host_Shield_2.0/blob/master/Usb.cpp
 *
 * This file is licenced under the GPL v2.
 *
 * Important note on worst-case (full-speed) packet size constraints
 * (See USB 2.0 Section 5.6.3 and following):
 *
 *	- control:	  64 bytes
 *	- isochronous:	1023 bytes
 *	- interrupt:	  64 bytes
 *	- bulk:		  64 bytes
 *
 * Since the MAX3421 FIFO size is 64 bytes, we do not have to work about
 * multi-FIFO writes/reads for a single USB packet *except* for isochronous
 * transfers.  We don't support isochronous transfers at this time, so we
 * just assume that a USB packet always fits into a single FIFO buffer.
 *
 * NOTE: The June 2006 version of "MAX3421E Programming Guide"
 * (AN3785) has conflicting info for the RCVDAVIRQ bit:
 *
 *	The description of RCVDAVIRQ says "The CPU *must* clear
 *	this IRQ bit (by writing a 1 to it) before reading the
 *	RCVFIFO data.
 *
 * However, the earlier section on "Programming BULK-IN
 * Transfers" says * that:
 *
 *	After the CPU retrieves the data, it clears the
 *	RCVDAVIRQ bit.
 *
 * The December 2006 version has been corrected and it consistently
 * states the second behavior is the correct one.
 *
 * Synchronous SPI transactions sleep so we can't perform any such
 * transactions while holding a spin-lock (and/or while interrupts are
 * masked).  To achieve this, all SPI transactions are issued from a
 * single thread (max3421_spi_thread).
 */

#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/of.h>

#include <linux/platform_data/max3421-hcd.h>

#define DRIVER_DESC
#define DRIVER_VERSION

/* 11-bit counter that wraps around (USB 2.0 Section 8.3.3): */
#define USB_MAX_FRAME_NUMBER
#define USB_MAX_RETRIES

#define POWER_BUDGET

/* Port-change mask: */
#define PORT_C_MASK

#define MAX3421_GPOUT_COUNT

enum max3421_rh_state {};

enum pkt_state {};

enum scheduling_pass {};

/* Bit numbers for max3421_hcd->todo: */
enum {};

struct max3421_dma_buf {};

struct max3421_hcd {};

struct max3421_ep {};

#define MAX3421_FIFO_SIZE

#define MAX3421_SPI_DIR_RD
#define MAX3421_SPI_DIR_WR

/* SPI commands: */
#define MAX3421_SPI_DIR_SHIFT
#define MAX3421_SPI_REG_SHIFT

#define MAX3421_REG_RCVFIFO
#define MAX3421_REG_SNDFIFO
#define MAX3421_REG_SUDFIFO
#define MAX3421_REG_RCVBC
#define MAX3421_REG_SNDBC
#define MAX3421_REG_USBIRQ
#define MAX3421_REG_USBIEN
#define MAX3421_REG_USBCTL
#define MAX3421_REG_CPUCTL
#define MAX3421_REG_PINCTL
#define MAX3421_REG_REVISION
#define MAX3421_REG_IOPINS1
#define MAX3421_REG_IOPINS2
#define MAX3421_REG_GPINIRQ
#define MAX3421_REG_GPINIEN
#define MAX3421_REG_GPINPOL
#define MAX3421_REG_HIRQ
#define MAX3421_REG_HIEN
#define MAX3421_REG_MODE
#define MAX3421_REG_PERADDR
#define MAX3421_REG_HCTL
#define MAX3421_REG_HXFR
#define MAX3421_REG_HRSL

enum {};

enum {};

enum {};

enum {};

enum {};

enum {};

enum {};

enum {};

/* Return same error-codes as ohci.h:cc_to_error: */
static const int hrsl_to_error[] =;

/*
 * See https://www.beyondlogic.org/usbnutshell/usb4.shtml#Control for a
 * reasonable overview of how control transfers use the IN/OUT
 * tokens.
 */
#define MAX3421_HXFR_BULK_IN(ep)
#define MAX3421_HXFR_SETUP
#define MAX3421_HXFR_BULK_OUT(ep)
#define MAX3421_HXFR_ISO_IN(ep)
#define MAX3421_HXFR_ISO_OUT(ep)
#define MAX3421_HXFR_HS_IN
#define MAX3421_HXFR_HS_OUT

#define field(val, bit)

static inline s16
frame_diff(u16 left, u16 right)
{}

static inline struct max3421_hcd *
hcd_to_max3421(struct usb_hcd *hcd)
{}

static inline struct usb_hcd *
max3421_to_hcd(struct max3421_hcd *max3421_hcd)
{}

static u8
spi_rd8(struct usb_hcd *hcd, unsigned int reg)
{}

static void
spi_wr8(struct usb_hcd *hcd, unsigned int reg, u8 val)
{}

static void
spi_rd_buf(struct usb_hcd *hcd, unsigned int reg, void *buf, size_t len)
{}

static void
spi_wr_buf(struct usb_hcd *hcd, unsigned int reg, void *buf, size_t len)
{}

/*
 * Figure out the correct setting for the LOWSPEED and HUBPRE mode
 * bits.  The HUBPRE bit needs to be set when MAX3421E operates at
 * full speed, but it's talking to a low-speed device (i.e., through a
 * hub).  Setting that bit ensures that every low-speed packet is
 * preceded by a full-speed PRE PID.  Possible configurations:
 *
 * Hub speed:	Device speed:	=>	LOWSPEED bit:	HUBPRE bit:
 *	FULL	FULL		=>	0		0
 *	FULL	LOW		=>	1		1
 *	LOW	LOW		=>	1		0
 *	LOW	FULL		=>	1		0
 */
static void
max3421_set_speed(struct usb_hcd *hcd, struct usb_device *dev)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static void
max3421_set_address(struct usb_hcd *hcd, struct usb_device *dev, int epnum)
{}

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

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

static int
max3421_transfer_out(struct usb_hcd *hcd, struct urb *urb, int fast_retransmit)
{}

/*
 * Issue the next host-transfer command.
 * Caller must NOT hold HCD spinlock.
 */
static void
max3421_next_transfer(struct usb_hcd *hcd, int fast_retransmit)
{}

/*
 * Find the next URB to process and start its execution.
 *
 * At this time, we do not anticipate ever connecting a USB hub to the
 * MAX3421 chip, so at most USB device can be connected and we can use
 * a simplistic scheduler: at the start of a frame, schedule all
 * periodic transfers.  Once that is done, use the remainder of the
 * frame to process non-periodic (bulk & control) transfers.
 *
 * Preconditions:
 * o Caller must NOT hold HCD spinlock.
 * o max3421_hcd->curr_urb MUST BE NULL.
 * o MAX3421E chip must be idle.
 */
static int
max3421_select_and_start_urb(struct usb_hcd *hcd)
{}

/*
 * Check all endpoints for URBs that got unlinked.
 *
 * Caller must NOT hold HCD spinlock.
 */
static int
max3421_check_unlink(struct usb_hcd *hcd)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static void
max3421_slow_retransmit(struct usb_hcd *hcd)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static void
max3421_recv_data_available(struct usb_hcd *hcd)
{}

static void
max3421_handle_error(struct usb_hcd *hcd, u8 hrsl)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static int
max3421_transfer_in_done(struct usb_hcd *hcd, struct urb *urb)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static int
max3421_transfer_out_done(struct usb_hcd *hcd, struct urb *urb)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static void
max3421_host_transfer_done(struct usb_hcd *hcd)
{}

/*
 * Caller must NOT hold HCD spinlock.
 */
static void
max3421_detect_conn(struct usb_hcd *hcd)
{}

static irqreturn_t
max3421_irq_handler(int irq, void *dev_id)
{}

#ifdef DEBUG

static void
dump_eps(struct usb_hcd *hcd)
{
	struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
	struct max3421_ep *max3421_ep;
	struct usb_host_endpoint *ep;
	char ubuf[512], *dp, *end;
	unsigned long flags;
	struct urb *urb;
	int epnum, ret;

	spin_lock_irqsave(&max3421_hcd->lock, flags);
	list_for_each_entry(max3421_ep, &max3421_hcd->ep_list, ep_list) {
		ep = max3421_ep->ep;

		dp = ubuf;
		end = dp + sizeof(ubuf);
		*dp = '\0';
		list_for_each_entry(urb, &ep->urb_list, urb_list) {
			ret = scnprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb,
					usb_pipetype(urb->pipe),
					usb_urb_dir_in(urb) ? "IN" : "OUT",
					urb->actual_length,
					urb->transfer_buffer_length);
			if (ret == end - dp - 1)
				break;	/* error or buffer full */
			dp += ret;
		}

		epnum = usb_endpoint_num(&ep->desc);
		pr_info("EP%0u %u lst %04u rtr %u nak %6u rxmt %u: %s\n",
			epnum, max3421_ep->pkt_state, max3421_ep->last_active,
			max3421_ep->retries, max3421_ep->naks,
			max3421_ep->retransmit, ubuf);
	}
	spin_unlock_irqrestore(&max3421_hcd->lock, flags);
}

#endif /* DEBUG */

/* Return zero if no work was performed, 1 otherwise.  */
static int
max3421_handle_irqs(struct usb_hcd *hcd)
{}

static int
max3421_reset_hcd(struct usb_hcd *hcd)
{}

static int
max3421_urb_done(struct usb_hcd *hcd)
{}

static int
max3421_spi_thread(void *dev_id)
{}

static int
max3421_reset_port(struct usb_hcd *hcd)
{}

static int
max3421_reset(struct usb_hcd *hcd)
{}

static int
max3421_start(struct usb_hcd *hcd)
{}

static void
max3421_stop(struct usb_hcd *hcd)
{}

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

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

static void
max3421_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
{}

static int
max3421_get_frame_number(struct usb_hcd *hcd)
{}

/*
 * Should return a non-zero value when any port is undergoing a resume
 * transition while the root hub is suspended.
 */
static int
max3421_hub_status_data(struct usb_hcd *hcd, char *buf)
{}

static inline void
hub_descriptor(struct usb_hub_descriptor *desc)
{}

/*
 * Set the MAX3421E general-purpose output with number PIN_NUMBER to
 * VALUE (0 or 1).  PIN_NUMBER may be in the range from 1-8.  For
 * any other value, this function acts as a no-op.
 */
static void
max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value)
{}

static int
max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
		    char *buf, u16 length)
{}

static int
max3421_bus_suspend(struct usb_hcd *hcd)
{}

static int
max3421_bus_resume(struct usb_hcd *hcd)
{}

static const struct hc_driver max3421_hcd_desc =;

static int
max3421_of_vbus_en_pin(struct device *dev, struct max3421_hcd_platform_data *pdata)
{}

static int
max3421_probe(struct spi_device *spi)
{}

static void
max3421_remove(struct spi_device *spi)
{}

static const struct of_device_id max3421_of_match_table[] =;
MODULE_DEVICE_TABLE(of, max3421_of_match_table);

static struct spi_driver max3421_driver =;

module_spi_driver();

MODULE_DESCRIPTION();
MODULE_AUTHOR();
MODULE_LICENSE();