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

// SPDX-License-Identifier: GPL-2.0
/*
 * ISP1362 HCD (Host Controller Driver) for USB.
 *
 * Copyright (C) 2005 Lothar Wassmann <[email protected]>
 *
 * Derived from the SL811 HCD, rewritten for ISP116x.
 * Copyright (C) 2005 Olav Kongas <[email protected]>
 *
 * Portions:
 * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
 * Copyright (C) 2004 David Brownell
 */

/*
 * The ISP1362 chip requires a large delay (300ns and 462ns) between
 * accesses to the address and data register.
 * The following timing options exist:
 *
 * 1. Configure your memory controller to add such delays if it can (the best)
 * 2. Implement platform-specific delay function possibly
 *    combined with configuring the memory controller; see
 *    include/linux/usb_isp1362.h for more info.
 * 3. Use ndelay (easiest, poorest).
 *
 * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the
 * platform specific section of isp1362.h to select the appropriate variant.
 *
 * Also note that according to the Philips "ISP1362 Errata" document
 * Rev 1.00 from 27 May data corruption may occur when the #WR signal
 * is reasserted (even with #CS deasserted) within 132ns after a
 * write cycle to any controller register. If the hardware doesn't
 * implement the recommended fix (gating the #WR with #CS) software
 * must ensure that no further write cycle (not necessarily to the chip!)
 * is issued by the CPU within this interval.

 * For PXA25x this can be ensured by using VLIO with the maximum
 * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz.
 */

#undef ISP1362_DEBUG

/*
 * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and
 * GET_INTERFACE requests correctly when the SETUP and DATA stages of the
 * requests are carried out in separate frames. This will delay any SETUP
 * packets until the start of the next frame so that this situation is
 * unlikely to occur (and makes usbtest happy running with a PXA255 target
 * device).
 */
#undef BUGGY_PXA2XX_UDC_USBTEST

#undef PTD_TRACE
#undef URB_TRACE
#undef VERBOSE
#undef REGISTERS

/* This enables a memory test on the ISP1362 chip memory to make sure the
 * chip access timing is correct.
 */
#undef CHIP_BUFFER_TEST

#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/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/usb/isp1362.h>
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/io.h>
#include <linux/bitmap.h>
#include <linux/prefetch.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>

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

static int dbg_level;
#ifdef ISP1362_DEBUG
module_param(dbg_level, int, 0644);
#else
module_param(dbg_level, int, 0);
#endif

#include "../core/usb.h"
#include "isp1362.h"


#define DRIVER_VERSION
#define DRIVER_DESC

MODULE_DESCRIPTION();
MODULE_LICENSE();

static const char hcd_name[] =;

static void isp1362_hc_stop(struct usb_hcd *hcd);
static int isp1362_hc_start(struct usb_hcd *hcd);

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

/*
 * When called from the interrupthandler only isp1362_hcd->irqenb is modified,
 * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon
 * completion.
 * We don't need a 'disable' counterpart, since interrupts will be disabled
 * only by the interrupt handler.
 */
static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask)
{}

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

static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd,
						     u16 offset)
{}

static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index)
{}

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

static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size,
				    int mps)
{}

static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
			     struct isp1362_ep *ep, u16 len)
{}

static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
{}

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

/*
  Set up PTD's.
*/
static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
			struct isp1362_ep *ep, struct isp1362_ep_queue *epq,
			u16 fno)
{}

static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
			      struct isp1362_ep_queue *epq)
{}

static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
			     struct isp1362_ep_queue *epq)
{}

/*
 * INT PTDs will stay in the chip until data is available.
 * This function will remove a PTD from the chip when the URB is dequeued.
 * Must be called with the spinlock held and IRQs disabled
 */
static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)

{}

/*
  Take done or failed requests out of schedule. Give back
  processed urbs.
*/
static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
			   struct urb *urb, int status)
     __releases(isp1362_hcd->lock)
     __acquires(isp1362_hcd->lock)
{}

/*
 * Analyze transfer results, handle partial transfers and errors
*/
static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
{}

static void finish_unlinks(struct isp1362_hcd *isp1362_hcd)
{}

static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count)
{}

static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd)
{}

static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip)
{}

static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
		      struct isp1362_ep *ep, struct isp1362_ep_queue *epq)
{}

static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd)
{}

static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd)
{}

static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
{}

static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd)
{}

static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map,
			     struct isp1362_ep_queue *epq)
{}

static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq)
{}

static irqreturn_t isp1362_irq(struct usb_hcd *hcd)
{}

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

#define MAX_PERIODIC_LOAD
static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load)
{}

/* NB! ALL the code above this point runs with isp1362_hcd->lock
   held, irqs off
*/

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

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

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

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

static int isp1362_get_frame(struct usb_hcd *hcd)
{}

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

/* Adapted from ohci-hub.c */
static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf)
{}

static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
				   struct usb_hub_descriptor *desc)
{}

/* Adapted from ohci-hub.c */
static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
			       u16 wIndex, char *buf, u16 wLength)
{}

#ifdef	CONFIG_PM
static int isp1362_bus_suspend(struct usb_hcd *hcd)
{}

static int isp1362_bus_resume(struct usb_hcd *hcd)
{}
#else
#define isp1362_bus_suspend
#define isp1362_bus_resume
#endif

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

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

static void dump_int(struct seq_file *s, char *label, u32 mask)
{}

static void dump_ctrl(struct seq_file *s, char *label, u32 mask)
{}

static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd)
{}

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

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

static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
{}

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

static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
{}

static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
{}

static int isp1362_mem_config(struct usb_hcd *hcd)
{}

static int isp1362_hc_reset(struct usb_hcd *hcd)
{}

static void isp1362_hc_stop(struct usb_hcd *hcd)
{}

#ifdef CHIP_BUFFER_TEST
static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd)
{
	int ret = 0;
	u16 *ref;
	unsigned long flags;

	ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL);
	if (ref) {
		int offset;
		u16 *tst = &ref[ISP1362_BUF_SIZE / 2];

		for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) {
			ref[offset] = ~offset;
			tst[offset] = offset;
		}

		for (offset = 0; offset < 4; offset++) {
			int j;

			for (j = 0; j < 8; j++) {
				spin_lock_irqsave(&isp1362_hcd->lock, flags);
				isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j);
				isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j);
				spin_unlock_irqrestore(&isp1362_hcd->lock, flags);

				if (memcmp(ref, tst, j)) {
					ret = -ENODEV;
					pr_err("%s: memory check with %d byte offset %d failed\n",
					    __func__, j, offset);
					dump_data((u8 *)ref + offset, j);
					dump_data((u8 *)tst + offset, j);
				}
			}
		}

		spin_lock_irqsave(&isp1362_hcd->lock, flags);
		isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE);
		isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
		spin_unlock_irqrestore(&isp1362_hcd->lock, flags);

		if (memcmp(ref, tst, ISP1362_BUF_SIZE)) {
			ret = -ENODEV;
			pr_err("%s: memory check failed\n", __func__);
			dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2);
		}

		for (offset = 0; offset < 256; offset++) {
			int test_size = 0;

			yield();

			memset(tst, 0, ISP1362_BUF_SIZE);
			spin_lock_irqsave(&isp1362_hcd->lock, flags);
			isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
			isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
			spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
			if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))),
				   ISP1362_BUF_SIZE / 2)) {
				pr_err("%s: Failed to clear buffer\n", __func__);
				dump_data((u8 *)tst, ISP1362_BUF_SIZE);
				break;
			}
			spin_lock_irqsave(&isp1362_hcd->lock, flags);
			isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE);
			isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref),
					     offset * 2 + PTD_HEADER_SIZE, test_size);
			isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
					    PTD_HEADER_SIZE + test_size);
			spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
			if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
				dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size);
				dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size);
				spin_lock_irqsave(&isp1362_hcd->lock, flags);
				isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
						    PTD_HEADER_SIZE + test_size);
				spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
				if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
					ret = -ENODEV;
					pr_err("%s: memory check with offset %02x failed\n",
					    __func__, offset);
					break;
				}
				pr_warn("%s: memory check with offset %02x ok after second read\n",
					__func__, offset);
			}
		}
		kfree(ref);
	}
	return ret;
}
#endif

static int isp1362_hc_start(struct usb_hcd *hcd)
{}

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

static const struct hc_driver isp1362_hc_driver =;

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

static void isp1362_remove(struct platform_device *pdev)
{}

static int isp1362_probe(struct platform_device *pdev)
{}

#ifdef	CONFIG_PM
static int isp1362_suspend(struct platform_device *pdev, pm_message_t state)
{}

static int isp1362_resume(struct platform_device *pdev)
{}
#else
#define isp1362_suspend
#define isp1362_resume
#endif

static struct platform_driver isp1362_driver =;

module_platform_driver();