/*
* core.c - ChipIdea USB IP core family device controller
*
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
*
* Author: David Lopo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* Description: ChipIdea USB IP core family device controller
*
* This driver is composed of several blocks:
* - HW: hardware interface
* - DBG: debug facilities (optional)
* - UTIL: utilities
* - ISR: interrupts handling
* - ENDPT: endpoint operations (Gadget API)
* - GADGET: gadget operations (Gadget API)
* - BUS: bus glue code, bus abstraction layer
*
* Compile Options
* - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities
* - STALL_IN: non-empty bulk-in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* => case 4: Hi > Dn
* => case 5: Hi > Di
* => case 8: Hi <> Do
* if undefined usbtest 13 fails
* - TRACE: enable function tracing (depends on DEBUG)
*
* Main Features
* - Chapter 9 & Mass Storage Compliance with Gadget File Storage
* - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
* - Normal & LPM support
*
* USBTEST Report
* - OK: 0-12, 13 (STALL_IN defined) & 14
* - Not Supported: 15 & 16 (ISO)
*
* TODO List
* - Suspend & Remote Wakeup
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/chipidea.h>
#include <linux/usb/of.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/ehci_def.h>
#include "ci.h"
#include "udc.h"
#include "bits.h"
#include "host.h"
#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
/* Controller register map */
static const u8 ci_regs_nolpm[] = {
[CAP_CAPLENGTH] = 0x00U,
[CAP_HCCPARAMS] = 0x08U,
[CAP_DCCPARAMS] = 0x24U,
[CAP_TESTMODE] = 0x38U,
[OP_USBCMD] = 0x00U,
[OP_USBSTS] = 0x04U,
[OP_USBINTR] = 0x08U,
[OP_DEVICEADDR] = 0x14U,
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0x64U,
[OP_USBMODE] = 0x68U,
[OP_ENDPTSETUPSTAT] = 0x6CU,
[OP_ENDPTPRIME] = 0x70U,
[OP_ENDPTFLUSH] = 0x74U,
[OP_ENDPTSTAT] = 0x78U,
[OP_ENDPTCOMPLETE] = 0x7CU,
[OP_ENDPTCTRL] = 0x80U,
};
static const u8 ci_regs_lpm[] = {
[CAP_CAPLENGTH] = 0x00U,
[CAP_HCCPARAMS] = 0x08U,
[CAP_DCCPARAMS] = 0x24U,
[CAP_TESTMODE] = 0xFCU,
[OP_USBCMD] = 0x00U,
[OP_USBSTS] = 0x04U,
[OP_USBINTR] = 0x08U,
[OP_DEVICEADDR] = 0x14U,
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0xC4U,
[OP_USBMODE] = 0xC8U,
[OP_ENDPTSETUPSTAT] = 0xD8U,
[OP_ENDPTPRIME] = 0xDCU,
[OP_ENDPTFLUSH] = 0xE0U,
[OP_ENDPTSTAT] = 0xE4U,
[OP_ENDPTCOMPLETE] = 0xE8U,
[OP_ENDPTCTRL] = 0xECU,
};
static void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
{
int i;
for (i = 0; i < OP_ENDPTCTRL; i++)
ci->hw_bank.regmap[i] =
(i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) +
(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
for (; i <= OP_LAST; i++)
ci->hw_bank.regmap[i] = ci->hw_bank.op +
4 * (i - OP_ENDPTCTRL) +
(is_lpm
? ci_regs_lpm[OP_ENDPTCTRL]
: ci_regs_nolpm[OP_ENDPTCTRL]);
}
static enum ci_revision ci_get_revision(struct ci_hdrc *ci)
{
int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION);
enum ci_revision rev = CI_REVISION_UNKNOWN;
if (ver == 0x2) {
rev = hw_read_id_reg(ci, ID_ID, REVISION)
>> __ffs(REVISION);
rev += CI_REVISION_20;
} else