Revert "C1: USB FIQ driver (Not ready)"

This commit is contained in:
ckkim
2015-10-28 14:45:23 +09:00
parent c5a11154db
commit 91517efe96
44 changed files with 559 additions and 4062 deletions

View File

@@ -110,7 +110,6 @@ void root_func(){
reg = <0xc4301000 0x1000
0xc4300100 0x0100>;
interrupt-controller;
gic_fiq_enable;
#interrupt-cells = <3>;
#address-cells = <0>;
};
@@ -876,7 +875,6 @@ sdio{
port-config = <0>; /** 0: default */
port-dma = <0>; /** 0: default, 1: single, 2: incr, 3: incr4, 4: incr8, 5: incr16, 6: disable*/
port-id-mode = <1>; /** 0: hardware, 1: sw_host, 2: sw_slave*/
fiq_use = <1>; /**0:no use 1: use**/
status = "okay";
};

View File

@@ -110,7 +110,6 @@ void root_func(){
reg = <0xc4301000 0x1000
0xc4300100 0x0100>;
interrupt-controller;
gic_fiq_enable;
#interrupt-cells = <3>;
#address-cells = <0>;
};
@@ -864,7 +863,6 @@ sdio{
port-config = <0>; /** 0: default */
port-dma = <0>; /** 0: default, 1: single, 2: incr, 3: incr4, 4: incr8, 5: incr16, 6: disable*/
port-id-mode = <1>; /** 0: hardware, 1: sw_host, 2: sw_slave*/
fiq_use = <1>; /**0:no use 1: use**/
status = "okay";
};

View File

@@ -99,7 +99,6 @@ void root_func(){
reg = <0xc4301000 0x1000
0xc4300100 0x0100>;
interrupt-controller;
gic_fiq_enable;
#interrupt-cells = <3>;
#address-cells = <0>;
};
@@ -853,7 +852,6 @@ void root_func(){
port-config = <0>; /** 0: default */
port-dma = <0>; /** 0: default, 1: single, 2: incr, 3: incr4, 4: incr8, 5: incr16, 6: disable*/
port-id-mode = <1>; /** 0: hardware, 1: sw_host, 2: sw_slave*/
fiq_use = <1>;
status = "okay";
};

View File

@@ -69,7 +69,6 @@
reg = <0xc4301000 0x1000
0xc4300100 0x0100>;
interrupt-controller;
gic_fiq_enable;
#interrupt-cells = <3>;
#address-cells = <0>;
};
@@ -366,7 +365,6 @@
port-dma = <0>; /** 0: default, 1: single, 2: incr, 3: incr4, 4: incr8, 5: incr16, 6: disable*/
port-id-mode = <1>; /** 0: hardware, 1: sw_host, 2: sw_slave*/
gpio-hub-rst = "GPIOAO_4";
fiq_use = <1>;
status = "okay";
};

View File

@@ -526,7 +526,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
CONFIG_AMLOGIC_MESON_CPUFREQ=y
CONFIG_FIX_SYSPLL=y
CONFIG_MESON_GIC_FIQ=y
# CONFIG_CPU_IDLE is not set
# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set

2
arch/arm/include/asm/fiq.h Executable file → Normal file
View File

@@ -38,12 +38,10 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
extern int get_fiq_index(void);
/* helpers defined in fiqasm.S: */
extern void __set_fiq_regs(unsigned long const *regs);
extern void __get_fiq_regs(unsigned long *regs);
extern void __FIQ_Branch(unsigned long *regs);
static inline void set_fiq_regs(struct pt_regs const *regs)
{

View File

@@ -22,7 +22,6 @@
#define GIC_DIST_CTRL 0x000
#define GIC_DIST_CTR 0x004
#define GIC_DIST_IGROUP 0x080
#define GIC_DIST_ENABLE_SET 0x100
#define GIC_DIST_ENABLE_CLEAR 0x180
#define GIC_DIST_PENDING_SET 0x200

9
arch/arm/kernel/fiq.c Executable file → Normal file
View File

@@ -53,7 +53,6 @@
})
static unsigned long no_fiq_insn;
static int fiqno = 0x4000;
/* Default reacquire function
* - we always relinquish FIQ control
@@ -135,19 +134,12 @@ static int fiq_start;
void enable_fiq(int fiq)
{
fiqno = fiq;
enable_irq(fiq + fiq_start);
}
void disable_fiq(int fiq)
{
disable_irq(fiq + fiq_start);
fiqno = 0x4000;
}
int get_fiq_index(void)
{
return fiqno;
}
EXPORT_SYMBOL(set_fiq_handler);
@@ -157,7 +149,6 @@ EXPORT_SYMBOL(claim_fiq);
EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
EXPORT_SYMBOL(disable_fiq);
EXPORT_SYMBOL(get_fiq_index);
void __init init_FIQ(int start)
{

View File

@@ -25,9 +25,6 @@
ENTRY(__set_fiq_regs)
mov r2, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
mrs r1, cpsr
@@@@@@@@@@@@@@@ hack: enable the fiq here to keep usb driver happy
and r1, #~PSR_F_BIT
@@@@@@@@@@@@@@@ endhack: (need to find better place for this to happen)
msr cpsr_c, r2 @ select FIQ mode
mov r0, r0 @ avoid hazard prior to ARMv4
ldmia r0!, {r8 - r12}
@@ -50,21 +47,3 @@ ENTRY(__get_fiq_regs)
mov r0, r0 @ avoid hazard prior to ARMv4
mov pc, lr
ENDPROC(__get_fiq_regs)
ENTRY(__FIQ_Branch)
mov pc, r8
ENDPROC(__FIQ_Branch)
ENTRY(__fiq_ll_setup)
stmdb sp!, {r4}
mrs r4, cpsr
@ and r4, #~PSR_F_BIT
msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
mov r8, r0
mov r9, r1
mov fp, r2
mov sp, r3
msr cpsr_c, r4
ldmia sp!, {r4}
mov pc, lr
ENDPROC(__fiq_ll_setup)

View File

@@ -70,10 +70,4 @@ config GPIO_TEST
help
support gpio group test
config MESON_GIC_FIQ
bool "MESON support GIC FIQ"
default n
depends on FIQ
help
support GIC FIQ
endif # ARCH_MESON8

View File

@@ -193,7 +193,7 @@
/* All interrupts are FIQ capable */
#define FIQ_START 0
#define FIQ_START AM_IRQ0(0)
extern void request_fiq(unsigned fiq, void (*isr)(void));
extern void free_fiq(unsigned fiq, void (*isr)(void));

View File

@@ -24,13 +24,6 @@ config MESON_TRUSTZONE
default n
help
Enable ARM Trustzone secure extension
config MESON_GIC_FIQ
bool "MESON support GIC FIQ"
default n
depends on FIQ
help
support GIC FIQ
endif # ARCH_MESON8

View File

@@ -193,7 +193,7 @@
/* All interrupts are FIQ capable */
#define FIQ_START 0
#define FIQ_START AM_IRQ0(0)
extern void request_fiq(unsigned fiq, void (*isr)(void));
extern void free_fiq(unsigned fiq, void (*isr)(void));

22
arch/arm/plat-meson/gic-irq.c Executable file → Normal file
View File

@@ -31,7 +31,6 @@
#include <asm/mach/time.h>
#include <asm/mach/irq.h>
#include <asm/hardware/gic.h>
#include <asm/fiq.h>
#include <plat/io.h>
#include <mach/io.h>
#ifdef CONFIG_OF
@@ -49,20 +48,19 @@ static void meson_gic_unmask(struct irq_data *data)
* Set irq to edge rising and proi to low
*/
uint32_t dist_base=(uint32_t)(IO_PERIPH_BASE+0x1000);
int edge = 0x3;//edge
int irq=data->irq;
int edge = 0x3;//edge
int irq=data->irq;
if(irq<32)
return;
if(irq >= FIQ_START)
irq -= FIQ_START;
/**
* Deal with IRQ type: IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH
*/
if(data->state_use_accessors & IRQ_TYPE_LEVEL_MASK)
edge = 0x1;//level
if((irq == 62)||(irq == 63))
if((irq == 62)||(irq == 63))
edge = 0x1;//level
/**
@@ -72,15 +70,10 @@ static void meson_gic_unmask(struct irq_data *data)
/**
* Set prority
*/
aml_set_reg32_bits(dist_base+GIC_DIST_PRI + (irq / 4)* 4,0xff,(irq%4)*8,irq_level);
if(data->irq == get_fiq_index())
aml_set_reg32_bits(dist_base + GIC_DIST_IGROUP + (irq / 32) * 4, 0, (irq%32), 1);
else
aml_set_reg32_bits(dist_base + GIC_DIST_IGROUP + (irq / 32) * 4, 1, (irq%32), 1);
}
#ifdef CONFIG_OF
static const struct of_device_id mesnon_dt_irq_match[] __initconst = {
{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init },
@@ -99,8 +92,9 @@ void __init meson_init_gic_irq(void)
aml_write_reg32(IO_PERIPH_BASE+0x100 +GIC_CPU_PRIMASK,0xff);
#ifdef CONFIG_MESON_GIC_FIQ
init_FIQ(FIQ_START);
#ifdef CONFIG_MESON_ARM_GIC_FIQ
extern void init_fiq(void) ;
init_fiq();
#endif
}

View File

@@ -25,8 +25,6 @@ $(DWC_OTG_NAME)-objs += dwc_otg_cil.o dwc_otg_cil_intr.o
$(DWC_OTG_NAME)-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o
$(DWC_OTG_NAME)-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o
$(DWC_OTG_NAME)-objs += dwc_otg_adp.o
$(DWC_OTG_NAME)-objs += dwc_otg_fiq_fsm.o
$(DWC_OTG_NAME)-objs += dwc_otg_fiq_stub.o
ifneq ($(CFI),)
$(DWC_OTG_NAME)-objs += dwc_otg_cfi.o
endif

View File

@@ -580,13 +580,7 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value)
void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
{
unsigned long flags;
local_irq_save(flags);
local_fiq_disable();
writel((readl(reg) & ~clear_mask) | set_mask, reg);
local_fiq_enable();
local_irq_restore(flags);
}
#if 0
@@ -997,11 +991,6 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
tasklet_schedule(&task->t);
}
void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
{
tasklet_hi_schedule(&task->t);
}
/* workqueues
- run in process context (can sleep)

View File

@@ -384,17 +384,17 @@ struct { \
#define DWC_TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define DWC_TAILQ_EMPTY(head) \
(DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
(TAILQ_FIRST(head) == TAILQ_END(head))
#define DWC_TAILQ_FOREACH(var, head, field) \
for ((var) = DWC_TAILQ_FIRST(head); \
(var) != DWC_TAILQ_END(head); \
(var) = DWC_TAILQ_NEXT(var, field))
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = DWC_TAILQ_LAST(head, headname); \
(var) != DWC_TAILQ_END(head); \
(var) = DWC_TAILQ_PREV(var, headname, field))
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.

View File

@@ -960,8 +960,6 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task);
extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
#define dwc_task_schedule DWC_TASK_SCHEDULE
extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
/** @name Timer
*

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_adp.c Executable file → Normal file
View File

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_attr.c Executable file → Normal file
View File

6
drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c Executable file → Normal file
View File

@@ -2444,9 +2444,9 @@ void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
hcchar.b.epnum = hc->ep_num;
hcchar.b.epdir = hc->ep_is_in;
hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
//if((hc->ep_type == DWC_OTG_EP_TYPE_INTR) && hc->do_split && (hc->speed == DWC_OTG_EP_SPEED_FULL))
// hcchar.b.eptype = DWC_OTG_EP_TYPE_BULK;
//else
if((hc->ep_type == DWC_OTG_EP_TYPE_INTR) && hc->do_split && (hc->speed == DWC_OTG_EP_SPEED_FULL))
hcchar.b.eptype = DWC_OTG_EP_TYPE_BULK;
else
hcchar.b.eptype = hc->ep_type;
hcchar.b.mps = hc->max_packet;

5
drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h Executable file → Normal file
View File

@@ -959,8 +959,6 @@ struct dwc_otg_core_if {
/* Host only flag */
int host_only;
int use_fiq_flag;
/** Device mode Periodic Tx FIFO Mask */
uint32_t p_tx_msk;
/** Device mode Periodic Tx FIFO Mask */
@@ -1391,8 +1389,7 @@ typedef struct dwc_otg_cil_callbacks {
/** Sleep (switch to L0 state) */
int (*sleep) (void *_p);
#endif
/** Pointer passed to start() and stop() */
void *p;
} dwc_otg_cil_callbacks_t;
extern void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * _core_if,

49
drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil_intr.c Executable file → Normal file
View File

@@ -1518,7 +1518,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
/**
* This function returns the Core Interrupt register.
*/
static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd)
static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if)
{
gahbcfg_data_t gahbcfg = {.d32 = 0 };
gintsts_data_t gintsts;
@@ -1535,45 +1535,26 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin
gintmsk_common.b.lpmtranrcvd = 1;
#endif
gintmsk_common.b.restoredone = 1;
if(dwc_otg_is_device_mode(core_if))
{
/** @todo: The port interrupt occurs while in device
* mode. Added code to CIL to clear the interrupt for now!
*/
gintmsk_common.b.portintr = 1;
}
gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
if(fiq_enable && core_if->use_fiq_flag) {
local_fiq_disable();
/* Pull in the interrupts that the FIQ has masked */
gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
gintmsk.d32 |= gintmsk_common.d32;
/* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
reenable_gintmsk->d32 = gintmsk.d32;
local_fiq_enable();
}
gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
#ifdef DEBUG
/* if any common interrupts set */
if (gintsts.d32 & gintmsk_common.d32) {
DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n",
DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n",
gintsts.d32, gintmsk.d32);
}
#endif
if (!(fiq_enable&&core_if->use_fiq_flag)){
if (gahbcfg.b.glblintrmsk)
return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
else
return 0;
} else {
/* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface.
* Can't trust the global interrupt mask bit in this case.
*/
return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
}
}
@@ -1605,7 +1586,7 @@ int32_t dwc_otg_handle_common_intr(void *dev)
{
int retval = 0;
gintsts_data_t gintsts;
gintmsk_data_t gintmsk_reenable = { .d32 = 0 };
gintsts_data_t gintsts_tmp;
gpwrdn_data_t gpwrdn = {.d32 = 0 };
dwc_otg_device_t *otg_dev = dev;
dwc_otg_core_if_t *core_if = otg_dev->core_if;
@@ -1633,10 +1614,7 @@ int32_t dwc_otg_handle_common_intr(void *dev)
}
if (core_if->hibernation_suspend <= 0) {
/* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end
* of this handler - god only knows why it's done like this
*/
gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd);
gintsts.d32 = dwc_otg_read_common_intr(core_if);
if (gintsts.b.modemismatch) {
retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
@@ -1648,7 +1626,15 @@ int32_t dwc_otg_handle_common_intr(void *dev)
retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
}
if (gintsts.b.disconnect) {
if (gintsts.b.portintr && dwc_otg_is_host_mode(core_if))
retval |= dwc_otg_handle_disconnect_intr(core_if);
else
{
gintsts_tmp.d32 = 0;
gintsts_tmp.b.disconnect = 1;
DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts_tmp.d32);
retval |= 1;
}
}
if (gintsts.b.sessreqintr) {
retval |= dwc_otg_handle_session_req_intr(core_if);
@@ -1732,17 +1718,8 @@ int32_t dwc_otg_handle_common_intr(void *dev)
gintsts.b.portintr = 1;
DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
retval |= 1;
gintmsk_reenable.b.portintr = 1;
}
/* Did we actually handle anything? if so, unmask the interrupt */
// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval);
// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32);
// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32);
if (retval && fiq_enable &&core_if->use_fiq_flag) {
DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32);
}
} else {
DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_core_if.h Executable file → Normal file
View File

View File

@@ -57,7 +57,6 @@
#include "dwc_otg_cil.h"
#include "dwc_otg_pcd_if.h"
#include "dwc_otg_hcd_if.h"
#include "dwc_otg_fiq_fsm.h"
#include <linux/of_platform.h>
#include <linux/amlogic/of_lm.h>
@@ -73,7 +72,6 @@
#define DWC_DRIVER_VERSION "3.10a 12-MAY-2014"
#define DWC_DRIVER_DESC "HS OTG USB Controller driver"
bool microframe_schedule=true;
static const char dwc_driver_name[] = "dwc_otg";
@@ -271,17 +269,6 @@ static const char *dma_config_name[] = {
"BURST_INCR16"
"DISABLE",
};
//Global variable to switch the fiq fix on or off
bool fiq_enable = 1;
// Global variable to enable the split transaction fix
bool fiq_fsm_enable = true;
//Bulk split-transaction NAK holdoff in microframes
uint16_t nak_holdoff = 8;
unsigned short fiq_fsm_mask = 0x07;
/**
* This function shows the Driver Version.
*/
@@ -905,7 +892,6 @@ static int dwc_otg_driver_probe(
int charger_detect = 0;
int host_only_core = 0;
int pmu_apply_power = 0;
int use_fiq_flag = 0;
unsigned int phy_reg_addr = 0;
unsigned int ctrl_reg_addr = 0;
const char *s_clock_name = NULL;
@@ -914,8 +900,6 @@ static int dwc_otg_driver_probe(
struct clk * clock;
dwc_otg_device_t *dwc_otg_device;
struct dwc_otg_driver_module_params *pcore_para;
int irqno;
unsigned long flags = IRQF_SHARED | IRQF_DISABLED;
dev_dbg(&_dev->dev, "dwc_otg_driver_probe(%p)\n", _dev);
@@ -949,15 +933,6 @@ static int dwc_otg_driver_probe(
struct device_node *of_node = _dev->dev.of_node;
match = of_lm_match_node(dwc_otg_dt_match, of_node);
if(match){
gpio_name = of_get_property(of_node, "gpio-pwr", NULL);
if (gpio_name) {
int nr = amlogic_gpio_name_map_num(gpio_name);
amlogic_gpio_request_one(nr,
GPIOF_OUT_INIT_LOW,
"usb_pwr_en");
amlogic_set_value(nr, 1, "usb_pwr_en");
}
s_clock_name = of_get_property(of_node, "clock-src", NULL);
prop = of_get_property(of_node, "port-id", NULL);
if(prop)
@@ -1017,11 +992,7 @@ static int dwc_otg_driver_probe(
prop = of_get_property(of_node, "pmu-apply-power", NULL);
if(prop)
pmu_apply_power = of_read_ulong(prop,1);
prop = of_get_property(of_node, "fiq_use", NULL);
if(prop)
use_fiq_flag = of_read_ulong(prop,1);
ctrl_reg_addr = (unsigned long)usb_platform_data.ctrl_regaddr[port_index];
phy_reg_addr = (unsigned long)usb_platform_data.phy_regaddr[port_index];
_dev->irq = usb_platform_data.irq_no[port_index];
@@ -1229,20 +1200,14 @@ static int dwc_otg_driver_probe(
*/
dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);
if(fiq_enable && use_fiq_flag) {
irqno= MESON_USB_FIQ_BRIDGE;
} else {
irqno = _dev->irq;
flags |= IRQ_TYPE_LEVEL_HIGH;
}
/*
* Install the interrupt handler for the common interrupts before
* enabling common interrupts in core_init below.
*/
DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n",
irqno);
retval = request_irq(irqno, dwc_otg_common_irq,
flags, "dwc_otg",
_dev->irq);
retval = request_irq(_dev->irq, dwc_otg_common_irq,
IRQF_SHARED | IRQF_DISABLED | IRQ_LEVEL, "dwc_otg",
dwc_otg_device);
if (retval) {
DWC_ERROR("request of irq%d failed\n", _dev->irq);
@@ -1252,6 +1217,11 @@ static int dwc_otg_driver_probe(
dwc_otg_device->common_irq_installed = 1;
}
if (irq_set_affinity(_dev->irq, cpumask_of(3))) {
pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
_dev->irq, 3);
}
#ifdef LM_INTERFACE
// set_irq_type(_dev->irq, IRQT_LOW);
#endif
@@ -1286,7 +1256,6 @@ static int dwc_otg_driver_probe(
if(host_only_core&&pmu_apply_power)
dwc_otg_device->core_if->swicth_int_reg = 1;
dwc_otg_device->core_if->use_fiq_flag = use_fiq_flag;
if (port_type == USB_PORT_TYPE_HOST) {
/*
* Initialize the HCD
@@ -1437,11 +1406,6 @@ static int __init dwc_otg_driver_init(void)
{
int retval = 0;
int error;
if(fiq_fsm_enable && !fiq_enable) {
printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n");
fiq_enable = 1;
}
printk(KERN_INFO "%s: version %s\n", dwc_driver_name,
DWC_DRIVER_VERSION);
#ifdef LM_INTERFACE
@@ -1453,10 +1417,6 @@ static int __init dwc_otg_driver_init(void)
printk(KERN_ERR "%s retval=%d\n", __func__, retval);
return retval;
}
printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");
#ifdef LM_INTERFACE
error = driver_create_file(&dwc_otg_driver.drv, &driver_attr_version);
error = driver_create_file(&dwc_otg_driver.drv, &driver_attr_debuglevel);
@@ -1740,22 +1700,6 @@ module_param_named(adp_enable, dwc_otg_module_params.adp_enable, int, 0444);
MODULE_PARM_DESC(adp_enable, "ADP Enable 0=ADP Disabled 1=ADP Enabled");
module_param_named(otg_ver, dwc_otg_module_params.otg_ver, int, 0444);
MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0");
module_param(microframe_schedule, bool, 0444);
MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler");
module_param(fiq_enable, bool, 0444);
MODULE_PARM_DESC(fiq_enable, "Enable the FIQ");
module_param(nak_holdoff, ushort, 0644);
MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8");
module_param(fiq_fsm_enable, bool, 0444);
MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask");
module_param(fiq_fsm_mask, ushort, 0444);
MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n"
"Bit 0 : Non-periodic split transactions\n"
"Bit 1 : Periodic split transactions\n"
"Bit 2 : High-speed multi-transfer isochronous\n"
"All other bits should be set 0.");
/** @page "Module Parameters"
*

1
drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h Executable file → Normal file
View File

@@ -46,7 +46,6 @@
struct dwc_otg_pcd;
struct dwc_otg_hcd;
#define MESON_USB_FIQ_BRIDGE INT_TIMER_D
/**
* This structure is a wrapper that encapsulates the driver components used to
* manage a single DWC_otg controller.

File diff suppressed because it is too large Load Diff

View File

@@ -1,354 +0,0 @@
/*
* dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions
*
* Copyright (c) 2013 Raspberry Pi Foundation
*
* Author: Jonathan Bell <jonathan@raspberrypi.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Raspberry Pi nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This FIQ implements functionality that performs split transactions on
* the dwc_otg hardware without any outside intervention. A split transaction
* is "queued" by nominating a specific host channel to perform the entirety
* of a split transaction. This FIQ will then perform the microframe-precise
* scheduling required in each phase of the transaction until completion.
*
* The FIQ functionality has been surgically implanted into the Synopsys
* vendor-provided driver.
*
*/
#ifndef DWC_OTG_FIQ_FSM_H_
#define DWC_OTG_FIQ_FSM_H_
#include "dwc_otg_regs.h"
#include "dwc_otg_cil.h"
#include "dwc_otg_hcd.h"
#include <linux/kernel.h>
#include <linux/irqflags.h>
#include <linux/string.h>
#include <asm/barrier.h>
#if 0
#define FLAME_ON(x) \
do { \
int gpioreg; \
\
gpioreg = readl(__io_address(0x20200000+0x8)); \
gpioreg &= ~(7 << (x-20)*3); \
gpioreg |= 0x1 << (x-20)*3; \
writel(gpioreg, __io_address(0x20200000+0x8)); \
\
writel(1<<x, __io_address(0x20200000+(0x1C))); \
} while (0)
#define FLAME_OFF(x) \
do { \
writel(1<<x, __io_address(0x20200000+(0x28))); \
} while (0)
#else
#define FLAME_ON(x) do { } while (0)
#define FLAME_OFF(X) do { } while (0)
#endif
/* This is a quick-and-dirty arch-specific register read/write. We know that
* writes to a peripheral on BCM2835 will always arrive in-order, also that
* reads and writes are executed in-order therefore the need for memory barriers
* is obviated if we're only talking to USB.
*/
#define FIQ_WRITE(_addr_,_data_) (*(volatile unsigned int *) (_addr_) = (_data_))
#define FIQ_READ(_addr_) (*(volatile unsigned int *) (_addr_))
/* FIQ-ified register definitions. Offsets are from dwc_regs_base. */
#define GINTSTS 0x014
#define GINTMSK 0x018
/* Debug register. Poll the top of the received packets FIFO. */
#define GRXSTSR 0x01C
#define HFNUM 0x408
#define HAINT 0x414
#define HAINTMSK 0x418
#define HPRT0 0x440
/* HC_regs start from an offset of 0x500 */
#define HC_START 0x500
#define HC_OFFSET 0x020
#define HC_DMA 0x514
#define HCCHAR 0x00
#define HCSPLT 0x04
#define HCINT 0x08
#define HCINTMSK 0x0C
#define HCTSIZ 0x10
#define ISOC_XACTPOS_ALL 0b11
#define ISOC_XACTPOS_BEGIN 0b10
#define ISOC_XACTPOS_MID 0b00
#define ISOC_XACTPOS_END 0b01
#define DWC_PID_DATA2 0b01
#define DWC_PID_MDATA 0b11
#define DWC_PID_DATA1 0b10
#define DWC_PID_DATA0 0b00
typedef struct {
volatile void* base;
volatile void* ctrl;
volatile void* outdda;
volatile void* outddb;
volatile void* intstat;
} mphi_regs_t;
enum fiq_debug_level {
FIQDBG_SCHED = (1 << 0),
FIQDBG_INT = (1 << 1),
FIQDBG_ERR = (1 << 2),
FIQDBG_PORTHUB = (1 << 3),
};
struct fiq_state;
extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...);
#if 0
#define fiq_print _fiq_print
#else
#define fiq_print(x, y, ...)
#endif
extern bool fiq_enable, fiq_fsm_enable;
extern ushort nak_holdoff;
/**
* enum fiq_fsm_state - The FIQ FSM states.
*
* This is the "core" of the FIQ FSM. Broadly, the FSM states follow the
* USB2.0 specification for host responses to various transaction states.
* There are modifications to this host state machine because of a variety of
* quirks and limitations in the dwc_otg hardware.
*
* The fsm state is also used to communicate back to the driver on completion of
* a split transaction. The end states are used in conjunction with the interrupts
* raised by the final transaction.
*/
enum fiq_fsm_state {
/* FIQ isn't enabled for this host channel */
FIQ_PASSTHROUGH = 0,
/* For the first interrupt received for this channel,
* the FIQ has to ack any interrupts indicating success. */
FIQ_PASSTHROUGH_ERRORSTATE = 31,
/* Nonperiodic state groups */
FIQ_NP_SSPLIT_STARTED = 1,
FIQ_NP_SSPLIT_RETRY = 2,
FIQ_NP_OUT_CSPLIT_RETRY = 3,
FIQ_NP_IN_CSPLIT_RETRY = 4,
FIQ_NP_SPLIT_DONE = 5,
FIQ_NP_SPLIT_LS_ABORTED = 6,
/* This differentiates a HS transaction error from a LS one
* (handling the hub state is different) */
FIQ_NP_SPLIT_HS_ABORTED = 7,
/* Periodic state groups */
/* Periodic transactions are either started directly by the IRQ handler
* or deferred if the TT is already in use.
*/
FIQ_PER_SSPLIT_QUEUED = 8,
FIQ_PER_SSPLIT_STARTED = 9,
FIQ_PER_SSPLIT_LAST = 10,
FIQ_PER_ISO_OUT_PENDING = 11,
FIQ_PER_ISO_OUT_ACTIVE = 12,
FIQ_PER_ISO_OUT_LAST = 13,
FIQ_PER_ISO_OUT_DONE = 27,
FIQ_PER_CSPLIT_WAIT = 14,
FIQ_PER_CSPLIT_NYET1 = 15,
FIQ_PER_CSPLIT_BROKEN_NYET1 = 28,
FIQ_PER_CSPLIT_NYET_FAFF = 29,
/* For multiple CSPLITs (large isoc IN, or delayed interrupt) */
FIQ_PER_CSPLIT_POLL = 16,
/* The last CSPLIT for a transaction has been issued, differentiates
* for the state machine to queue the next packet.
*/
FIQ_PER_CSPLIT_LAST = 17,
FIQ_PER_SPLIT_DONE = 18,
FIQ_PER_SPLIT_LS_ABORTED = 19,
FIQ_PER_SPLIT_HS_ABORTED = 20,
FIQ_PER_SPLIT_NYET_ABORTED = 21,
/* Frame rollover has occurred without the transaction finishing. */
FIQ_PER_SPLIT_TIMEOUT = 22,
/* FIQ-accelerated HS Isochronous state groups */
FIQ_HS_ISOC_TURBO = 23,
/* For interval > 1, SOF wakes up the isochronous FSM */
FIQ_HS_ISOC_SLEEPING = 24,
FIQ_HS_ISOC_DONE = 25,
FIQ_HS_ISOC_ABORTED = 26,
FIQ_DEQUEUE_ISSUED = 30,
FIQ_TEST = 32,
};
struct fiq_stack {
int magic1;
uint8_t stack[2048];
int magic2;
};
/**
* struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel)
* @index: Number of slots reported used for IN transactions / number of slots
* transmitted for an OUT transaction
* @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused)
*
* Split transaction transfers can have variable length depending on other bus
* traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore
* each transaction needs a guaranteed aligned address. A maximum of 6 split transfers
* can happen per-frame.
*/
struct fiq_dma_info {
u8 index;
u8 slot_len[6];
};
struct __attribute__((packed)) fiq_split_dma_slot {
u8 buf[188];
};
struct fiq_dma_channel {
struct __attribute__((packed)) fiq_split_dma_slot index[6];
};
struct fiq_dma_blob {
struct __attribute__((packed)) fiq_dma_channel channel[0];
};
/**
* struct fiq_hs_isoc_info - USB2.0 isochronous data
* @iso_frame: Pointer to the array of OTG URB iso_frame_descs.
* @nrframes: Total length of iso_frame_desc array
* @index: Current index (FIQ-maintained)
*
*/
struct fiq_hs_isoc_info {
struct dwc_otg_hcd_iso_packet_desc *iso_desc;
unsigned int nrframes;
unsigned int index;
};
/**
* struct fiq_channel_state - FIQ state machine storage
* @fsm: Current state of the channel as understood by the FIQ
* @nr_errors: Number of transaction errors on this split-transaction
* @hub_addr: SSPLIT/CSPLIT destination hub
* @port_addr: SSPLIT/CSPLIT destination port - always 1 if single TT hub
* @nrpackets: For isoc OUT, the number of split-OUT packets to transmit. For
* split-IN, number of CSPLIT data packets that were received.
* @hcchar_copy:
* @hcsplt_copy:
* @hcintmsk_copy:
* @hctsiz_copy: Copies of the host channel registers.
* For use as scratch, or for returning state.
*
* The fiq_channel_state is state storage between interrupts for a host channel. The
* FSM state is stored here. Members of this structure must only be set up by the
* driver prior to enabling the FIQ for this host channel, and not touched until the FIQ
* has updated the state to either a COMPLETE state group or ABORT state group.
*/
struct fiq_channel_state {
enum fiq_fsm_state fsm;
unsigned int nr_errors;
unsigned int hub_addr;
unsigned int port_addr;
/* Hardware bug workaround: sometimes channel halt interrupts are
* delayed until the next SOF. Keep track of when we expected to get interrupted. */
unsigned int expected_uframe;
/* in/out for communicating number of dma buffers used, or number of ISOC to do */
unsigned int nrpackets;
struct fiq_dma_info dma_info;
struct fiq_hs_isoc_info hs_isoc_info;
/* Copies of HC registers - in/out communication from/to IRQ handler
* and for ease of channel setup. A bit of mungeing is performed - for
* example the hctsiz.b.maxp is _always_ the max packet size of the endpoint.
*/
hcchar_data_t hcchar_copy;
hcsplt_data_t hcsplt_copy;
hcint_data_t hcint_copy;
hcintmsk_data_t hcintmsk_copy;
hctsiz_data_t hctsiz_copy;
hcdma_data_t hcdma_copy;
};
/**
* struct fiq_state - top-level FIQ state machine storage
* @mphi_regs: virtual address of the MPHI peripheral register file
* @dwc_regs_base: virtual address of the base of the DWC core register file
* @dma_base: physical address for the base of the DMA bounce buffers
* @dummy_send: Scratch area for sending a fake message to the MPHI peripheral
* @gintmsk_saved: Top-level mask of interrupts that the FIQ has not handled.
* Used for determining which interrupts fired to set off the IRQ handler.
* @haintmsk_saved: Mask of interrupts from host channels that the FIQ did not handle internally.
* @np_count: Non-periodic transactions in the active queue
* @np_sent: Count of non-periodic transactions that have completed
* @next_sched_frame: For periodic transactions handled by the driver's SOF-driven queuing mechanism,
* this is the next frame on which a SOF interrupt is required. Used to hold off
* passing SOF through to the driver until necessary.
* @channel[n]: Per-channel FIQ state. Allocated during init depending on the number of host
* channels configured into the core logic.
*
* This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub.
* It contains top-level state information.
*/
struct fiq_state {
mphi_regs_t mphi_regs;
void *dwc_regs_base;
dma_addr_t dma_base;
struct fiq_dma_blob *fiq_dmab;
void *dummy_send;
gintmsk_data_t gintmsk_saved;
haintmsk_data_t haintmsk_saved;
int mphi_int_count;
unsigned int fiq_done;
unsigned int kick_np_queues;
unsigned int next_sched_frame;
#ifdef FIQ_DEBUG
char * buffer;
unsigned int bufsiz;
#endif
struct fiq_channel_state channel[0];
};
extern int fiq_fsm_too_late(struct fiq_state *st, int n);
extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n);
extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels);
extern void dwc_otg_fiq_nop(struct fiq_state *state);
extern void set_fiq_init(unsigned int fiq,long data);
#endif /* DWC_OTG_FIQ_FSM_H_ */

View File

@@ -1,81 +0,0 @@
/*
* dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ
*
* Copyright (c) 2013 Raspberry Pi Foundation
*
* Author: Jonathan Bell <jonathan@raspberrypi.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Raspberry Pi nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <asm/assembler.h>
#include <linux/linkage.h>
.text
.global _dwc_otg_fiq_stub_end;
/**
* _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow
* a C-style function call with arguments from the FIQ banked registers.
* r0 = &hcd->fiq_state
* r1 = &hcd->num_channels
* r2 = &hcd->dma_buffers
* Tramples: r0, r1, r2, r4, fp, ip
*/
ENTRY(_dwc_otg_fiq_stub)
/* Stash unbanked regs - SP will have been set up for us */
mov ip, sp;
stmdb sp!, {r0-r12, lr};
#ifdef FIQ_DEBUG
// Cycle profiling - read cycle counter at start
mrc p15, 0, r5, c15, c12, 1;
#endif
/* r11 = fp, don't trample it */
mov r4, fp;
/* set EABI frame size */
sub fp, ip, #512;
/* for fiq NOP mode - just need state */
mov r0, r8;
/* r9 = num_channels */
mov r1, r9;
/* r10 = struct *dma_bufs */
// mov r2, r10;
/* r4 = &fiq_c_function */
blx r4;
#ifdef FIQ_DEBUG
mrc p15, 0, r4, c15, c12, 1;
subs r5, r5, r4;
// r5 is now the cycle count time for executing the FIQ. Store it somewhere?
#endif
ldmia sp!, {r0-r12, lr};
subs pc, lr, #4;
_dwc_otg_fiq_stub_end:
END(_dwc_otg_fiq_stub)

1036
drivers/amlogic/usb/dwc_otg/310/dwc_otg_hcd.c Executable file → Normal file

File diff suppressed because it is too large Load Diff

92
drivers/amlogic/usb/dwc_otg/310/dwc_otg_hcd.h Executable file → Normal file
View File

@@ -40,8 +40,6 @@
#include "dwc_otg_core_if.h"
#include "dwc_list.h"
#include "dwc_otg_cil.h"
#include "dwc_otg_fiq_fsm.h"
/**
* @file
@@ -86,10 +84,21 @@ struct dwc_otg_hcd_urb {
uint32_t packet_count;
uint32_t flags;
uint16_t interval;
uint8_t qh_state;
#define URB_STATE_IDLE 1 /* QH is not being used */
#define URB_STATE_ACTIVE 2 /* QH is on the schedule */
#define URB_STATE_SETED 3 /* QH had finished setting reg */
#define URB_STATE_DQUEUE 4 /* QH had been pushed into tasklet, just used for isoc */
#define URB_STATE_UNLINK 5 /* QH has been removed from the schedule */
struct dwc_otg_hcd_pipe_info pipe_info;
struct dwc_otg_hcd_iso_packet_desc iso_descs[0];
};
typedef struct dwc_otg_hcd_urb_list{
struct dwc_otg_hcd_urb * urb;
dwc_list_link_t urb_list_entry;
} dwc_otg_hcd_urb_list_t;
static inline uint8_t dwc_otg_hcd_get_ep_num(struct dwc_otg_hcd_pipe_info *pipe)
{
return pipe->ep_num;
@@ -170,10 +179,10 @@ typedef enum dwc_otg_control_phase {
/** Transaction types. */
typedef enum dwc_otg_transaction_type {
DWC_OTG_TRANSACTION_NONE = 0,
DWC_OTG_TRANSACTION_PERIODIC = 1,
DWC_OTG_TRANSACTION_NON_PERIODIC = 2,
DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC
DWC_OTG_TRANSACTION_NONE,
DWC_OTG_TRANSACTION_PERIODIC,
DWC_OTG_TRANSACTION_NON_PERIODIC,
DWC_OTG_TRANSACTION_ALL
} dwc_otg_transaction_type_e;
struct dwc_otg_qh;
@@ -323,11 +332,6 @@ typedef struct dwc_otg_qh {
*/
uint16_t sched_frame;
/*
** Frame a NAK was received on this queue head, used to minimise NAK retransmission
*/
uint16_t nak_frame;
/** (micro)frame at which last start split was initialized. */
uint16_t start_split_frame;
@@ -370,22 +374,10 @@ typedef struct dwc_otg_qh {
struct dwc_otg_hcd_urb *dwc_otg_urb;
/** @} */
uint16_t speed;
uint16_t frame_usecs[8];
uint32_t skip_count;
} dwc_otg_qh_t;
DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
typedef struct urb_tq_entry {
struct urb *urb;
DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
} urb_tq_entry_t;
DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
/**
* This structure holds the state of the HCD, including the non-periodic and
* periodic schedules.
@@ -486,19 +478,6 @@ struct dwc_otg_hcd {
*/
uint16_t periodic_usecs;
/**
* Total bandwidth claimed so far for all periodic transfers
* in a frame.
* This will include a mixture of HS and FS transfers.
* Units are microseconds per (micro)frame.
* We have a budget per frame and have to schedule
* transactions accordingly.
* Watch out for the fact that things are actually scheduled for the
* "next frame".
*/
uint16_t frame_usecs[8];
/**
* Frame number read from the core at SOF. The value ranges from 0 to
* DWC_HFNUM_MAX_FRNUM.
@@ -521,17 +500,12 @@ struct dwc_otg_hcd {
* transaction and at least one host channel available for
* non-periodic transactions.
*/
int periodic_channels; /* microframe_schedule==0 */
int periodic_channels;
/**
* Number of host channels assigned to non-periodic transfers.
*/
int non_periodic_channels; /* microframe_schedule==0 */
/**
* Number of host channels assigned to non-periodic transfers.
*/
int available_host_channels;
int non_periodic_channels;
/**
* Array of pointers to the host channel descriptors. Allows accessing
@@ -562,13 +536,13 @@ struct dwc_otg_hcd {
/* Tasket to do a reset */
dwc_tasklet_t *reset_tasklet;
dwc_tasklet_t *isoc_complete_tasklet;
dwc_tasklet_t *completion_tasklet;
struct urb_list completed_urb_list;
dwc_list_link_t isoc_comp_urbs_list;
/* */
dwc_spinlock_t *lock;
dwc_spinlock_t *channel_lock;
dwc_spinlock_t * isoc_comp_urbs_lock;
void * isoc_comp_urbs[MAX_EPS_CHANNELS];
/**
* Private data that could be used by OS wrapper.
*/
@@ -579,21 +553,9 @@ struct dwc_otg_hcd {
/** Frame List */
uint32_t *frame_list;
/** Hub - Port assignment */
int hub_port[128];
#ifdef FIQ_DEBUG
int hub_port_alloc[2048];
#endif
/** Frame List DMA address */
dma_addr_t frame_list_dma;
struct fiq_stack *fiq_stack;
struct fiq_state *fiq_state;
/** Virtual address for split transaction DMA bounce buffers */
struct fiq_dma_blob *fiq_dmab;
#ifdef DEBUG
uint32_t frrem_samples;
uint64_t frrem_accum;
@@ -612,8 +574,8 @@ struct dwc_otg_hcd {
uint32_t hfnum_other_samples_b;
uint64_t hfnum_other_frrem_accum_b;
#endif
uint8_t ssplit_lock;
uint8_t auto_pm_suspend_flag;
uint8_t pm_freeze_flag;
};
/** @name Transaction Execution Functions */
@@ -623,13 +585,6 @@ extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t
extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
dwc_otg_transaction_type_e tr_type);
int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh);
void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh);
extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh);
extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num);
/** @} */
/** @name Interrupt Handler Functions */
@@ -869,5 +824,4 @@ void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
#define dwc_sample_frrem(_hcd, _qh, _letter)
#endif
#endif
#endif
/* DWC_DEVICE_ONLY */
#endif /* DWC_DEVICE_ONLY */

View File

@@ -39,8 +39,6 @@
#include "dwc_otg_hcd.h"
#include "dwc_otg_regs.h"
extern bool microframe_schedule;
static inline uint8_t frame_list_idx(uint16_t frame)
{
return (frame & (MAX_FRLIST_EN_NUM - 1));
@@ -275,18 +273,10 @@ void dump_frame_list(dwc_otg_hcd_t * hcd)
static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
{
dwc_irqflags_t flags;
dwc_spinlock_t *channel_lock = hcd->channel_lock;
dwc_hc_t *hc = qh->channel;
if (dwc_qh_is_non_per(qh)) {
DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
if (!microframe_schedule)
if (dwc_qh_is_non_per(qh))
hcd->non_periodic_channels--;
else
hcd->available_host_channels++;
DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
} else
update_frame_list(hcd, qh, 0);
/*
@@ -368,7 +358,7 @@ void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
release_channel_ddma(hcd, qh);
if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)
&& (microframe_schedule || !hcd->periodic_channels) && hcd->frame_list) {
&& !hcd->periodic_channels && hcd->frame_list) {
per_sched_disable(hcd);
frame_list_free(hcd);

7
drivers/amlogic/usb/dwc_otg/310/dwc_otg_hcd_if.h Executable file → Normal file
View File

@@ -79,6 +79,8 @@ struct dwc_otg_hcd_function_ops {
dwc_otg_hcd_hub_info_from_urb_cb_t hub_info;
dwc_otg_hcd_speed_from_urb_cb_t speed;
dwc_otg_hcd_complete_urb_cb_t complete;
dwc_otg_hcd_complete_urb_cb_t complete_in_tasklet;
dwc_otg_hcd_complete_urb_cb_t hcd_isoc_complete;
dwc_otg_hcd_get_b_hnp_enable get_b_hnp_enable;
};
/** @} */
@@ -113,11 +115,6 @@ extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd);
*/
extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
/** This function is used to handle the fast interrupt
*
*/
extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void);
/**
* Returns private data set by
* dwc_otg_hcd_set_priv_data function.

710
drivers/amlogic/usb/dwc_otg/310/dwc_otg_hcd_intr.c Executable file → Normal file
View File

@@ -35,99 +35,38 @@
#include "dwc_otg_hcd.h"
#include "dwc_otg_regs.h"
#include <linux/jiffies.h>
#include <mach/hardware.h>
#include <asm/fiq.h>
extern bool microframe_schedule;
/** @file
* This file contains the implementation of the HCD Interrupt handlers.
*/
int fiq_done, int_done;
#ifdef FIQ_DEBUG
char buffer[1000*16];
int wptr;
void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...)
{
FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB;
va_list args;
char text[17];
hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) };
if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR)
{
local_fiq_disable();
snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937);
va_start(args, fmt);
vsnprintf(text+8, 9, fmt, args);
va_end(args);
memcpy(buffer + wptr, text, 16);
wptr = (wptr + 16) % sizeof(buffer);
local_fiq_enable();
}
}
#endif
/** This function handles interrupts for the HCD. */
int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
{
int retval = 0;
static int last_time;
dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
gintsts_data_t gintsts;
gintmsk_data_t gintmsk;
haintmsk_data_t haintmsk;
#ifdef DEBUG
dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
#endif
DWC_SPINLOCK(dwc_otg_hcd->lock);
gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
if (dwc_otg_check_haps_status(core_if) == -1 ) {
DWC_WARN("HAPS is disconnected");
goto exit_handler_routine;
return retval;
}
/* Exit from ISR if core is hibernated */
if (core_if->hibernation_suspend == 1) {
goto exit_handler_routine;
return retval;
}
DWC_SPINLOCK(dwc_otg_hcd->lock);
/* Check if HOST Mode */
if (dwc_otg_is_host_mode(core_if)) {
if (fiq_enable&&dwc_otg_hcd->core_if->use_fiq_flag) {
local_fiq_disable();
/* Pull in from the FIQ's disabled mask */
gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32);
dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0;
}
if (fiq_fsm_enable && dwc_otg_hcd->core_if->use_fiq_flag && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) {
gintsts.b.hcintr = 1;
}
/* Danger will robinson: fake a SOF if necessary */
if (fiq_fsm_enable && dwc_otg_hcd->core_if->use_fiq_flag && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) {
gintsts.b.sofintr = 1;
}
gintsts.d32 &= gintmsk.d32;
if (fiq_enable&&dwc_otg_hcd->core_if->use_fiq_flag)
local_fiq_enable();
gintsts.d32 = dwc_otg_read_core_intr(core_if);
if (!gintsts.d32) {
goto exit_handler_routine;
DWC_SPINUNLOCK(dwc_otg_hcd->lock);
return 0;
}
#ifdef DEBUG
/* Don't print debug message in the interrupt handler on SOF */
#ifndef DEBUG_SOF
@@ -144,6 +83,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
"DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n",
gintsts.d32);
#endif
if (gintsts.b.sofintr) {
retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
}
@@ -161,10 +101,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
/** @todo Implement i2cintr handler. */
}
if (gintsts.b.portintr) {
gintmsk_data_t gintmsk = { .b.portintr = 1};
retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
}
if (gintsts.b.hcintr) {
retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd);
@@ -196,50 +133,6 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
#endif
}
exit_handler_routine:
if (fiq_enable && dwc_otg_hcd->core_if->use_fiq_flag) {
gintmsk_data_t gintmsk_new;
haintmsk_data_t haintmsk_new;
local_fiq_disable();
gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32;
if(fiq_fsm_enable)
haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32;
else
haintmsk_new.d32 = 0x0000FFFF;
/* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */
if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) {
#if 0
DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16));
if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) {
fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR");
DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16)));
while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17)))
;
DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31));
dwc_otg_hcd->fiq_state->mphi_int_count = 0;
}
#endif
int_done++;
}
haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
/* Re-enable interrupts that the FIQ masked (first time round) */
FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32);
local_fiq_enable();
if ((jiffies / HZ) > last_time) {
//dwc_otg_qh_t *qh;
//dwc_list_link_t *cur;
/* Once a second output the fiq and irq numbers, useful for debug */
last_time = jiffies / HZ;
// DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d",
// dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels,
// dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done);
//printk(KERN_WARNING "Periodic queues:\n");
}
}
DWC_SPINUNLOCK(dwc_otg_hcd->lock);
return retval;
}
@@ -247,22 +140,20 @@ exit_handler_routine:
#ifdef DWC_TRACK_MISSED_SOFS
#warning Compiling code to track missed SOFs
#define FRAME_NUM_ARRAY_SIZE 1000
/**
* This function is for debug only.
*/
void track_missed_sofs(uint16_t curr_frame_number)
static inline void track_missed_sofs(uint16_t curr_frame_number)
{
static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM;
static int dumped_frame_num_array = 0;
static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE];
static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE];
static int frame_num_idx = 0;
static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM;
static int dumped_frame_num_array = 0;
if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
if ((((last_frame_num + 1) & 0x3fff) !=
curr_frame_number)&&(last_frame_num!=curr_frame_number)){
if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) !=
curr_frame_number) {
frame_num_array[frame_num_idx] = curr_frame_number;
last_frame_num_array[frame_num_idx++] = last_frame_num;
}
@@ -289,12 +180,10 @@ void track_missed_sofs(uint16_t curr_frame_number)
int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
{
hfnum_data_t hfnum;
gintsts_data_t gintsts = { .d32 = 0 };
dwc_list_link_t *qh_entry;
dwc_otg_qh_t *qh;
dwc_otg_transaction_type_e tr_type;
int did_something = 0;
int32_t next_sched_frame = -1;
gintsts_data_t gintsts = {.d32 = 0 };
hfnum.d32 =
DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
@@ -324,31 +213,17 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
*/
DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
&qh->qh_list_entry);
did_something = 1;
}
else
{
if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame))
{
next_sched_frame = qh->sched_frame;
}
}
}
if (fiq_enable&&hcd->core_if->use_fiq_flag)
hcd->fiq_state->next_sched_frame = next_sched_frame;
tr_type = dwc_otg_hcd_select_transactions(hcd);
if (tr_type != DWC_OTG_TRANSACTION_NONE) {
dwc_otg_hcd_queue_transactions(hcd, tr_type);
did_something = 1;
}
/* Clear interrupt - but do not trample on the FIQ sof */
if (!(fiq_fsm_enable&&hcd->core_if->use_fiq_flag)) {
/* Clear interrupt */
gintsts.b.sofintr = 1;
DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32);
}
return 1;
}
@@ -638,24 +513,13 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
{
int i;
int retval = 0;
haint_data_t haint = { .d32 = 0 } ;
haint_data_t haint;
/* Clear appropriate bits in HCINTn to clear the interrupt bit in
* GINTSTS */
if (!(fiq_fsm_enable&&dwc_otg_hcd->core_if->use_fiq_flag))
haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
// Overwrite with saved interrupts from fiq handler
if(fiq_fsm_enable&&dwc_otg_hcd->core_if->use_fiq_flag)
{
/* check the mask? */
local_fiq_disable();
haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
local_fiq_enable();
}
for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
if (haint.b2.chint & (1 << i)) {
retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i);
@@ -754,7 +618,7 @@ static int update_urb_state_xfer_comp(dwc_hc_t * hc,
&& (urb->actual_length == urb->length)
&& !(urb->length % hc->max_packet)) {
xfer_done = 0;
} else if (short_read || urb->actual_length >= urb->length) {
} else if (short_read || urb->actual_length == urb->length) {
xfer_done = 1;
urb->status = 0;
}else if(urb->actual_length > urb->length){
@@ -831,7 +695,9 @@ update_isoc_urb_state(dwc_otg_hcd_t * hcd,
dwc_otg_hcd_urb_t *urb = qtd->urb;
dwc_otg_halt_status_e ret_val = halt_status;
struct dwc_otg_hcd_iso_packet_desc *frame_desc;
#if 0
dwc_otg_hcd_urb_list_t * urb_list;
#endif
frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
switch (halt_status) {
case DWC_OTG_HC_XFER_COMPLETE:
@@ -883,12 +749,33 @@ update_isoc_urb_state(dwc_otg_hcd_t * hcd,
DWC_ASSERT(1, "Unhandled _halt_status (%d)\n", halt_status);
break;
}
if (++qtd->isoc_frame_index == urb->packet_count) {
if ((++qtd->isoc_frame_index == urb->packet_count) ||(urb->qh_state == URB_STATE_DQUEUE)){
/*
* urb->status is not used for isoc transfers.
* The individual frame_desc statuses are used instead.
*/
hcd->fops->complete(hcd, urb->priv, urb, 0);
#if 0
urb_list = DWC_ALLOC(sizeof(dwc_otg_hcd_urb_list_t));
if (urb_list == NULL) {
DWC_ASSERT(1, "No memory alloc in update_isoc_urb_state\n");
}
urb_list->urb = urb;
DWC_SPINLOCK(hcd->isoc_comp_urbs_lock);
DWC_LIST_INSERT_TAIL(&hcd->isoc_comp_urbs_list, &urb_list->urb_list_entry);
DWC_SPINUNLOCK(hcd->isoc_comp_urbs_lock);
#else
int i;
for(i = 0; i < MAX_EPS_CHANNELS; i++){
if(hcd->isoc_comp_urbs[i] == NULL){
hcd->isoc_comp_urbs[i] = urb->priv;
break;
}
}
hcd->fops->hcd_isoc_complete(hcd,urb->priv,urb,0);
DWC_TASK_SCHEDULE(hcd->isoc_complete_tasklet);
#endif
ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;
} else {
ret_val = DWC_OTG_HC_XFER_COMPLETE;
@@ -946,22 +833,9 @@ static void release_channel(dwc_otg_hcd_t * hcd,
{
dwc_otg_transaction_type_e tr_type;
int free_qtd;
dwc_irqflags_t flags;
dwc_spinlock_t *channel_lock = hcd->channel_lock;
int hog_port = 0;
DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n",
__func__, hc->hc_num, halt_status, hc->xfer_len);
if(fiq_fsm_enable && hcd->core_if->use_fiq_flag && hc->do_split) {
if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) {
if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID ||
hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) {
hog_port = 0;
}
}
}
DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n",
__func__, hc->hc_num, halt_status);
switch (halt_status) {
case DWC_OTG_HC_XFER_URB_COMPLETE:
@@ -1015,12 +889,9 @@ cleanup:
* function clears the channel interrupt enables and conditions, so
* there's no need to clear the Channel Halted interrupt separately.
*/
if (fiq_fsm_enable && hcd->core_if->use_fiq_flag && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH)
dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num);
dwc_otg_hc_cleanup(hcd->core_if, hc);
DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
if (!microframe_schedule) {
switch (hc->ep_type) {
case DWC_OTG_EP_TYPE_CONTROL:
case DWC_OTG_EP_TYPE_BULK:
@@ -1035,13 +906,6 @@ cleanup:
*/
break;
}
} else {
DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
hcd->available_host_channels++;
fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels);
DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
}
/* Try to queue more transfers now that there's a free channel. */
tr_type = dwc_otg_hcd_select_transactions(hcd);
@@ -1256,6 +1120,8 @@ static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t * hcd,
*/
if (hc->qh->do_split) {
if(hc->ep_type == DWC_OTG_EP_TYPE_INTR)
hcd->ssplit_lock = 0;
if ((hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hc->ep_is_in
&& hcd->core_if->dma_enable) {
if (qtd->complete_split
@@ -1378,6 +1244,9 @@ static int32_t handle_hc_stall_intr(dwc_otg_hcd_t * hcd,
goto handle_stall_done;
}
if(pipe_type == UE_INTERRUPT)
hcd->ssplit_lock = 0;
if (pipe_type == UE_CONTROL) {
hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
}
@@ -1457,17 +1326,6 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
"NAK Received--\n", hc->hc_num);
/*
* When we get bulk NAKs then remember this so we holdoff on this qh until
* the beginning of the next frame
*/
switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
case UE_BULK:
case UE_CONTROL:
if (nak_holdoff && qtd->qh->do_split)
hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd);
}
/*
* Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
* interrupt. Re-start the SSPLIT transfer.
@@ -1476,6 +1334,11 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
if (hc->complete_split) {
qtd->error_count = 0;
}
if((hcd->ssplit_lock == dwc_otg_hcd_get_dev_addr(&qtd->urb->pipe_info)) &&
(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info) == UE_INTERRUPT))
hcd->ssplit_lock = 0;
qtd->complete_split = 0;
halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
goto handle_nak_done;
@@ -1490,11 +1353,7 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
* transfers in DMA mode for the sole purpose of
* resetting the error count after a transaction error
* occurs. The core will continue transferring data.
* Disable other interrupts unmasked for the same
* reason.
*/
disable_hc_int(hc_regs, datatglerr);
disable_hc_int(hc_regs, ack);
qtd->error_count = 0;
goto handle_nak_done;
}
@@ -1606,15 +1465,6 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd,
halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
}
} else {
/*
* An unmasked ACK on a non-split DMA transaction is
* for the sole purpose of resetting error counts. Disable other
* interrupts unmasked for the same reason.
*/
if(hcd->core_if->dma_enable) {
disable_hc_int(hc_regs, datatglerr);
disable_hc_int(hc_regs, nak);
}
qtd->error_count = 0;
if (hc->qh->ping_state) {
@@ -1677,10 +1527,8 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd,
hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
int frnum = dwc_otg_hcd_get_frame_number(hcd);
// With the FIQ running we only ever see the failed NYET
if (dwc_full_frame_num(frnum) !=
dwc_full_frame_num(hc->qh->sched_frame) ||
(fiq_fsm_enable&&hcd->core_if->use_fiq_flag)) {
dwc_full_frame_num(hc->qh->sched_frame)) {
/*
* No longer in the same full speed frame.
* Treat this as a transaction error.
@@ -1695,6 +1543,7 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd,
qtd->error_count++;
#endif
qtd->complete_split = 0;
hcd->ssplit_lock = 0;
halt_channel(hcd, hc, qtd,
DWC_OTG_HC_XFER_XACT_ERR);
/** @todo add support for isoc release */
@@ -1899,8 +1748,10 @@ static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * hcd,
break;
case UE_INTERRUPT:
qtd->error_count++;
if (hc->do_split && hc->complete_split) {
if (hc->do_split){
if(hc->complete_split)
qtd->complete_split = 0;
hcd->ssplit_lock = 0;
}
halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
break;
@@ -1967,28 +1818,13 @@ static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd,
dwc_otg_qtd_t * qtd)
{
DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
"Data Toggle Error on %s transfer--\n",
hc->hc_num, (hc->ep_is_in ? "IN" : "OUT"));
"Data Toggle Error--\n", hc->hc_num);
/* Data toggles on split transactions cause the hc to halt.
* restart transfer */
if(hc->qh->do_split)
{
qtd->error_count++;
dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
update_urb_state_xfer_intr(hc, hc_regs,
qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
} else if (hc->ep_is_in) {
/* An unmasked data toggle error on a non-split DMA transaction is
* for the sole purpose of resetting error counts. Disable other
* interrupts unmasked for the same reason.
*/
if(hcd->core_if->dma_enable) {
disable_hc_int(hc_regs, ack);
disable_hc_int(hc_regs, nak);
}
if (hc->ep_is_in) {
qtd->error_count = 0;
} else {
DWC_ERROR("Data Toggle Error on OUT transfer,"
"channel %d\n", hc->hc_num);
}
disable_hc_int(hc_regs, datatglerr);
@@ -2141,8 +1977,6 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.frmovrun) {
handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.datatglerr) {
handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
} else if (!out_nak_enh) {
if (hcint.b.nyet) {
/*
@@ -2182,6 +2016,8 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
("%s: Halt channel %d (assume incomplete periodic transfer)\n",
__func__, hc->hc_num);
#endif
if (hc->do_split && (hc->ep_type == DWC_OTG_EP_TYPE_INTR))
hcd->ssplit_lock = 0;
halt_channel(hcd, hc, qtd,
DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE);
} else {
@@ -2192,24 +2028,14 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
DWC_READ_REG32(&hcd->
core_if->core_global_regs->
gintsts));
/* Failthrough: use 3-strikes rule */
qtd->error_count++;
dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
update_urb_state_xfer_intr(hc, hc_regs,
qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
clear_hc_int(hc_regs, chhltd);
}
}
} else {
DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n",
hcint.d32);
/* Failthrough: use 3-strikes rule */
qtd->error_count++;
dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
update_urb_state_xfer_intr(hc, hc_regs,
qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
clear_hc_int(hc_regs, chhltd);
}
}
@@ -2246,367 +2072,6 @@ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
return 1;
}
/**
* dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on
* FIQ transfer completion
* @hcd: Pointer to dwc_otg_hcd struct
* @num: Host channel number
*
* 1. Un-mangle the status as recorded in each iso_frame_desc status
* 2. Copy it from the dwc_otg_urb into the real URB
*/
void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num)
{
struct dwc_otg_hcd_urb *dwc_urb = qtd->urb;
int nr_frames = dwc_urb->packet_count;
int i;
hcint_data_t frame_hcint;
for (i = 0; i < nr_frames; i++) {
frame_hcint.d32 = dwc_urb->iso_descs[i].status;
if (frame_hcint.b.xfercomp) {
dwc_urb->iso_descs[i].status = 0;
dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length;
} else if (frame_hcint.b.frmovrun) {
if (qh->ep_is_in)
dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES;
else
dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION;
dwc_urb->error_count++;
dwc_urb->iso_descs[i].actual_length = 0;
} else if (frame_hcint.b.xacterr) {
dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL;
dwc_urb->error_count++;
dwc_urb->iso_descs[i].actual_length = 0;
} else if (frame_hcint.b.bblerr) {
dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW;
dwc_urb->error_count++;
dwc_urb->iso_descs[i].actual_length = 0;
} else {
/* Something went wrong */
dwc_urb->iso_descs[i].status = -1;
dwc_urb->iso_descs[i].actual_length = 0;
dwc_urb->error_count++;
}
}
//printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n",
// __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count);
hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0);
release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
}
/**
* dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions
* @hcd: Pointer to dwc_otg_hcd struct
* @num: Host channel number
*
* Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state.
* Returns total length of data or -1 if the buffers were not used.
*
*/
int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num)
{
dwc_hc_t *hc = qh->channel;
struct fiq_dma_blob *blob = hcd->fiq_dmab;
struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
uint8_t *ptr = NULL;
int index = 0, len = 0;
int i = 0;
if (hc->ep_is_in) {
/* Copy data out of the DMA bounce buffers to the URB's buffer.
* The align_buf is ignored as this is ignored on FSM enqueue. */
ptr = qtd->urb->buf;
if (qh->ep_type == UE_ISOCHRONOUS) {
/* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */
index = qtd->isoc_frame_index;
ptr += qtd->urb->iso_descs[index].offset;
} else {
/* Need to increment by actual_length for interrupt IN */
ptr += qtd->urb->actual_length;
}
for (i = 0; i < st->dma_info.index; i++) {
len += st->dma_info.slot_len[i];
dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]);
ptr += st->dma_info.slot_len[i];
}
return len;
} else {
/* OUT endpoints - nothing to do. */
return -1;
}
}
/**
* dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt
* from a channel handled in the FIQ
* @hcd: Pointer to dwc_otg_hcd struct
* @num: Host channel number
*
* If a host channel interrupt was received by the IRQ and this was a channel
* used by the FIQ, the execution flow for transfer completion is substantially
* different from the normal (messy) path. This function and its friends handles
* channel cleanup and transaction completion from a FIQ transaction.
*/
int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num)
{
struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
dwc_hc_t *hc = hcd->hc_ptr_array[num];
dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
dwc_otg_qh_t *qh = hc->qh;
dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num];
hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy;
int hostchannels = 0;
int ret = 0;
fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm);
hostchannels = hcd->available_host_channels;
switch (st->fsm) {
case FIQ_TEST:
break;
case FIQ_DEQUEUE_ISSUED:
/* hc_halt was called. QTD no longer exists. */
/* TODO: for a nonperiodic split transaction, need to issue a
* CLEAR_TT_BUFFER hub command if we were in the start-split phase.
*/
release_channel(hcd, hc, NULL, hc->halt_status);
ret = 1;
break;
case FIQ_NP_SPLIT_DONE:
/* Nonperiodic transaction complete. */
if (!hc->ep_is_in) {
qtd->ssplit_out_xfer_count = hc->xfer_len;
}
if (hcint.b.xfercomp) {
handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.nak) {
handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
}
ret = 1;
break;
case FIQ_NP_SPLIT_HS_ABORTED:
/* A HS abort is a 3-strikes on the HS bus at any point in the transaction.
* Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that
* because there's no guarantee which order a non-periodic split happened in.
* We could end up clearing a perfectly good transaction out of the buffer.
*/
if (hcint.b.xacterr) {
qtd->error_count += st->nr_errors;
handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.ahberr) {
handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
} else {
local_fiq_disable();
BUG();
}
break;
case FIQ_NP_SPLIT_LS_ABORTED:
/* A few cases can cause this - either an unknown state on a SSPLIT or
* STALL/data toggle error response on a CSPLIT */
if (hcint.b.stall) {
handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.datatglerr) {
handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.bblerr) {
handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.ahberr) {
handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
} else {
local_fiq_disable();
BUG();
}
break;
case FIQ_PER_SPLIT_DONE:
/* Isoc IN or Interrupt IN/OUT */
/* Flow control here is different from the normal execution by the driver.
* We need to completely ignore most of the driver's method of handling
* split transactions and do it ourselves.
*/
if (hc->ep_type == UE_INTERRUPT) {
if (hcint.b.nak) {
handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
} else if (hc->ep_is_in) {
int len;
len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num);
//printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length);
qtd->urb->actual_length += len;
if (qtd->urb->actual_length >= qtd->urb->length) {
qtd->urb->status = 0;
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
/* Interrupt transfer not complete yet - is it a short read? */
if (len < hc->max_packet) {
/* Interrupt transaction complete */
qtd->urb->status = 0;
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
/* Further transactions required */
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
}
}
} else {
/* Interrupt OUT complete. */
dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
qtd->urb->actual_length += hc->xfer_len;
if (qtd->urb->actual_length >= qtd->urb->length) {
qtd->urb->status = 0;
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
}
}
} else {
/* ISOC IN complete. */
struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
int len = 0;
/* Record errors, update qtd. */
if (st->nr_errors) {
frame_desc->actual_length = 0;
frame_desc->status = -DWC_E_PROTOCOL;
} else {
frame_desc->status = 0;
/* Unswizzle dma */
len = dwc_otg_fiq_unsetup_per_dma(hcd, qh, qtd, num);
frame_desc->actual_length = len;
}
qtd->isoc_frame_index++;
if (qtd->isoc_frame_index == qtd->urb->packet_count) {
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
}
}
break;
case FIQ_PER_ISO_OUT_DONE: {
struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
/* Record errors, update qtd. */
if (st->nr_errors) {
frame_desc->actual_length = 0;
frame_desc->status = -DWC_E_PROTOCOL;
} else {
frame_desc->status = 0;
frame_desc->actual_length = frame_desc->length;
}
qtd->isoc_frame_index++;
qtd->isoc_split_offset = 0;
if (qtd->isoc_frame_index == qtd->urb->packet_count) {
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
}
}
break;
case FIQ_PER_SPLIT_NYET_ABORTED:
/* Doh. lost the data. */
printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed "
"- FIQ reported NYET. Data may have been lost.\n",
hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3);
if (hc->ep_type == UE_ISOCHRONOUS) {
struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
/* Record errors, update qtd. */
frame_desc->actual_length = 0;
frame_desc->status = -DWC_E_PROTOCOL;
qtd->isoc_frame_index++;
qtd->isoc_split_offset = 0;
if (qtd->isoc_frame_index == qtd->urb->packet_count) {
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
}
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
}
break;
case FIQ_HS_ISOC_DONE:
/* The FIQ has performed a whole pile of isochronous transactions.
* The status is recorded as the interrupt state should the transaction
* fail.
*/
dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num);
break;
case FIQ_PER_SPLIT_LS_ABORTED:
if (hcint.b.xacterr) {
/* Hub has responded with an ERR packet. Device
* has been unplugged or the port has been disabled.
* TODO: need to issue a reset to the hub port. */
qtd->error_count += 3;
handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.stall) {
handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
} else if (hcint.b.bblerr) {
handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
} else {
printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed "
"- FIQ reported FSM=%d. Data may have been lost.\n",
st->fsm, hc->dev_addr, hc->ep_num);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
}
break;
case FIQ_PER_SPLIT_HS_ABORTED:
/* Either the SSPLIT phase suffered transaction errors or something
* unexpected happened.
*/
qtd->error_count += 3;
handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
break;
case FIQ_PER_SPLIT_TIMEOUT:
/* Couldn't complete in the nominated frame */
printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed "
"- FIQ timed out. Data may have been lost.\n",
hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3);
if (hc->ep_type == UE_ISOCHRONOUS) {
struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
/* Record errors, update qtd. */
frame_desc->actual_length = 0;
if (hc->ep_is_in) {
frame_desc->status = -DWC_E_NO_STREAM_RES;
} else {
frame_desc->status = -DWC_E_COMMUNICATION;
}
qtd->isoc_frame_index++;
if (qtd->isoc_frame_index == qtd->urb->packet_count) {
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
}
} else {
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
}
break;
default:
local_fiq_disable();
DWC_WARN("unexpected state received on hc=%d fsm=%d", hc->hc_num, st->fsm);
BUG();
}
//if (hostchannels != hcd->available_host_channels) {
/* should have incremented by now! */
// BUG();
// }
return ret;
}
/** Handles interrupt for a specific Host Channel */
int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
{
@@ -2621,38 +2086,8 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
hc = dwc_otg_hcd->hc_ptr_array[num];
hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
/* We are responding to a channel disable. Driver
* state is cleared - our qtd has gone away.
*/
release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status);
return 1;
}
qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
/*
* FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ.
* Execution path is fundamentally different for the channels after a FIQ has completed
* a split transaction.
*/
if (fiq_fsm_enable&&dwc_otg_hcd->core_if->use_fiq_flag) {
switch (dwc_otg_hcd->fiq_state->channel[num].fsm) {
case FIQ_PASSTHROUGH:
break;
case FIQ_PASSTHROUGH_ERRORSTATE:
/* Hook into the error count */
fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num);
if (!dwc_otg_hcd->fiq_state->channel[num].nr_errors) {
qtd->error_count = 0;
fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET ");
}
break;
default:
dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num);
return 1;
}
}
hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
DWC_DEBUGPL(DBG_HCDV,
@@ -2689,7 +2124,6 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
}
if (hcint.b.ack) {
if(!hcint.b.chhltd)
retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
}
if (hcint.b.nyet) {

View File

@@ -50,10 +50,7 @@
#include <linux/dma-mapping.h>
#include <linux/version.h>
#include <asm/io.h>
#include <asm/fiq.h>
#include <linux/usb.h>
#include <linux/io.h>
#include <mach/am_regs.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
#include <../drivers/usb/core/hcd.h>
#else
@@ -64,12 +61,6 @@
#include "dwc_otg_dbg.h"
#include "dwc_otg_driver.h"
#include "dwc_otg_hcd.h"
extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end;
DEFINE_PER_CPU(void *, fiq_stack_cpu);
static DEFINE_MUTEX(fiq_lock);
/**
* Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
* qualified with its direction (possible 32 endpoints per device).
@@ -79,8 +70,6 @@ static DEFINE_MUTEX(fiq_lock);
static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
extern bool fiq_enable;
/** @name Linux HC Driver API Functions */
/** @{ */
static int urb_enqueue(struct usb_hcd *hcd,
@@ -234,6 +223,77 @@ static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
hcd_to_bus(hcd)->bandwidth_int_reqs--;
}
}
int _hcd_isoc_complete(dwc_otg_hcd_t * hcd,void *urb_handle,
dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
{
struct urb *urb = (struct urb *)urb_handle;
if(dwc_otg_urb->qh_state == URB_STATE_DQUEUE)
status = -ENOENT;
urb->actual_length = dwc_otg_urb->actual_length;
/* Convert status value. */
switch (status) {
case -DWC_E_PROTOCOL:
status = -EPROTO;
break;
case -DWC_E_IN_PROGRESS:
status = -EINPROGRESS;
break;
case -DWC_E_PIPE:
status = -EPIPE;
break;
case -DWC_E_IO:
status = -EIO;
break;
case -DWC_E_TIMEOUT:
status = -ETIMEDOUT;
break;
case -DWC_E_OVERFLOW:
status = -EOVERFLOW;
break;
default:
if (status) {
printk(KERN_DEBUG "%s:Uknown urb status %d\n",__func__, status);
}
}
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
int i;
urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb);
for (i = 0; i < urb->number_of_packets; ++i) {
urb->iso_frame_desc[i].actual_length = dwc_otg_urb->iso_descs[i].actual_length;
urb->iso_frame_desc[i].status = dwc_otg_urb->iso_descs[i].status;
}
}
urb->status = status;
if (!status) {
if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
(urb->actual_length < urb->transfer_buffer_length)) {
urb->status = -EREMOTEIO;
}
}
if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ||
(usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
if (ep) {
free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd),
dwc_otg_hcd_get_ep_bandwidth(hcd,
ep->hcpriv),
urb);
}
}
DWC_FREE(dwc_otg_urb);
return 0;
}
/**
* Sets the final status of an URB and returns it to the device driver. Any
* required cleanup of the URB is performed.
@@ -242,8 +302,6 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
{
struct urb *urb = (struct urb *)urb_handle;
urb_tq_entry_t *new_entry;
int rc = 0;
#ifdef DEBUG
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
@@ -259,7 +317,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
}
}
#endif
new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
/* Convert status value. */
switch (status) {
@@ -304,6 +362,108 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
}
}
urb->status = status;
if (!status) {
if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
(urb->actual_length < urb->transfer_buffer_length)) {
urb->status = -EREMOTEIO;
}
}
if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ||
(usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
if (ep) {
free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd),
dwc_otg_hcd_get_ep_bandwidth(hcd,
ep->hcpriv),
urb);
}
}
#if 0
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
#endif
DWC_FREE(dwc_otg_urb);
dwc_otg_urb = NULL;
DWC_SPINUNLOCK(hcd->lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
#else
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
#endif
DWC_SPINLOCK(hcd->lock);
return 0;
}
/*copy from _complete,not use spinlock*/
/**
* Sets the final status of an URB and returns it to the device driver. Any
* required cleanup of the URB is performed.
*/
int _complete_in_tasklet(dwc_otg_hcd_t * hcd, void *urb_handle,
dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
{
struct urb *urb = (struct urb *)urb_handle;
#if 0
#ifdef DEBUG
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
__func__, urb, usb_pipedevice(urb->pipe),
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "IN" : "OUT", status);
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
int i;
for (i = 0; i < urb->number_of_packets; i++) {
DWC_PRINTF(" ISO Desc %d status: %d\n",
i, urb->iso_frame_desc[i].status);
}
}
}
#endif
urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
/* Convert status value. */
switch (status) {
case -DWC_E_PROTOCOL:
status = -EPROTO;
break;
case -DWC_E_IN_PROGRESS:
status = -EINPROGRESS;
break;
case -DWC_E_PIPE:
status = -EPIPE;
break;
case -DWC_E_IO:
status = -EIO;
break;
case -DWC_E_TIMEOUT:
status = -ETIMEDOUT;
break;
case -DWC_E_OVERFLOW:
status = -EOVERFLOW;
break;
default:
if (status) {
DWC_PRINTF("Uknown urb status %d\n", status);
}
}
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
int i;
urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb);
for (i = 0; i < urb->number_of_packets; ++i) {
urb->iso_frame_desc[i].actual_length =
dwc_otg_hcd_urb_get_iso_desc_actual_length
(dwc_otg_urb, i);
urb->iso_frame_desc[i].status =
dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_urb, i);
}
}
urb->status = status;
urb->hcpriv = NULL;
if (!status) {
@@ -323,37 +483,19 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
urb);
}
}
#if 0
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
#endif
DWC_FREE(dwc_otg_urb);
if (!new_entry) {
DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
urb->status = -EPROTO;
/* don't schedule the tasklet -
* directly return the packet here with error. */
#if 0
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
#endif
DWC_SPINUNLOCK(hcd->lock);
//DWC_SPINUNLOCK(hcd->lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
#else
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
#endif
DWC_SPINLOCK(hcd->lock);
} else {
new_entry->urb = urb;
#if 0
rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
if(0 == rc) {
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
}
#endif
if(0 == rc) {
DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
urb_tq_entries);
DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
}
}
//DWC_SPINLOCK(hcd->lock);
return 0;
}
@@ -363,25 +505,11 @@ static struct dwc_otg_hcd_function_ops hcd_fops = {
.hub_info = _hub_info,
.speed = _speed,
.complete = _complete,
//.complete_in_tasklet = _complete_in_tasklet,
//.hcd_isoc_complete = _hcd_isoc_complete,
.complete_in_tasklet = _complete_in_tasklet,
.hcd_isoc_complete = _hcd_isoc_complete,
.get_b_hnp_enable = _get_b_hnp_enable,
};
static struct fiq_handler fh = {
.name = "usb_fiq",
};
extern void __fiq_ll_setup(long r8, long r9, long fp, void *sp);
static void fiq_setup_helper(void *regs)
{
struct pt_regs *fiq_regs = regs;
__fiq_ll_setup(fiq_regs->ARM_r8, fiq_regs->ARM_r9, fiq_regs->ARM_fp,
__get_cpu_var(fiq_stack_cpu) + THREAD_START_SP);
}
/**
* Initializes the HCD. This function allocates memory for and initializes the
* static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
@@ -403,11 +531,8 @@ int hcd_init(
#elif defined(PCI_INTERFACE)
dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
#endif
int retval = 0;
int irqno;
struct pt_regs regs;
unsigned long flags = IRQF_SHARED | IRQF_DISABLED;
int cpu;
DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n");
@@ -464,74 +589,6 @@ int hcd_init(
goto error2;
}
if (fiq_enable && dwc_otg_hcd->core_if->use_fiq_flag)
{
if (claim_fiq(&fh)) {
DWC_ERROR("Can't claim FIQ");
goto error2;
}
DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop));
DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub));
memset(&regs,0,sizeof(regs));
regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state;
if (fiq_fsm_enable) {
regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels;
//regs.ARM_r10 = dwc_otg_hcd->dma;
regs.ARM_fp = (long) dwc_otg_fiq_fsm;
DWC_WARN("FIQ r8=0x%08x r9=0x%08x", (unsigned int)regs.ARM_r8,(unsigned int)regs.ARM_r9);
} else {
regs.ARM_fp = (long) dwc_otg_fiq_nop;
}
mutex_lock(&fiq_lock);
for_each_possible_cpu(cpu) {
void *stack;
stack = (void *) __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
if (WARN_ON(!stack)) {
retval = -ENOMEM;
mutex_unlock(&fiq_lock);
goto error3;
}
per_cpu(fiq_stack_cpu, cpu) = stack;
}
on_each_cpu(fiq_setup_helper, &regs, true);
set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub);
mutex_unlock(&fiq_lock);
// __show_regs(&regs);
//Set the mphi periph to the required registers
//dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base;
//dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c;
//dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28;
//dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c;
//dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50;
dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base;
//DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base);
//Enable mphi peripheral
//writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl);
#ifdef DEBUG
//if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000)
// DWC_WARN("MPHI periph has been enabled");
//else
// DWC_WARN("MPHI periph has NOT been enabled");
#endif
//clear Timer D register
aml_clr_reg32_mask(P_ISA_TIMER_MUX, ((3<<6)|(1<<15)|(1<<19)));
aml_set_reg32_mask(P_ISA_TIMER_MUX, ((0<<6) | (0<<15) | (1<<19)));
printk("enable fiq %d\n",_dev->irq);
set_fiq_init(_dev->irq,(long)dwc_otg_hcd);
local_fiq_enable();
}
otg_dev->hcd->otg_dev = otg_dev;
hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel)
@@ -544,32 +601,19 @@ int hcd_init(
// if (otg_dev->core_if->otg_ver)
// hcd->self.is_hnp_cap = dwc_otg_get_hnpcapable(otg_dev->core_if);
#endif
if (fiq_enable && dwc_otg_hcd->core_if->use_fiq_flag) {
irqno = MESON_USB_FIQ_BRIDGE;
} else {
irqno = _dev->irq;
flags |= IRQ_TYPE_LEVEL_HIGH;
}
/*
* Finish generic HCD initialization and start the HCD. This function
* allocates the DMA buffer pool, registers the USB bus, requests the
* IRQ line, and calls hcd_start method.
*/
retval = usb_add_hcd(hcd, irqno, flags);
retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED);
if (retval < 0) {
goto error3;
goto error2;
}
dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd);
return 0;
error3:
mutex_lock(&fiq_lock);
for_each_possible_cpu(cpu) {
__free_pages(per_cpu(fiq_stack_cpu, cpu), THREAD_SIZE_ORDER);
per_cpu(fiq_stack_cpu, cpu) = NULL;
}
mutex_unlock(&fiq_lock);
error2:
usb_put_hcd(hcd);
error1:
@@ -673,13 +717,11 @@ int hcd_suspend(struct usb_hcd *hcd)
{
dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
dwc_otg_hcd->auto_pm_suspend_flag = (hcd->flags>>31)&1;
dwc_otg_hcd->pm_freeze_flag = (hcd->flags >> 30) & 1;
DWC_DEBUGPL(DBG_HCD, "HCD SUSPEND\n");
dwc_otg_hcd_suspend(dwc_otg_hcd);
hcd->flags &= (~(1<<31));
hcd->flags &= (~(1<<30));
return 0;
}
@@ -689,26 +731,19 @@ int hcd_resume(struct usb_hcd *hcd)
{
dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
dwc_otg_hcd->auto_pm_suspend_flag = (hcd->flags>>31)&1;
dwc_otg_hcd->pm_freeze_flag = (hcd->flags >> 30) & 1;
DWC_DEBUGPL(DBG_HCD, "HCD RESUME\n");
dwc_otg_hcd_resume(dwc_otg_hcd);
hcd->flags &= (~(1<<31));
hcd->flags &= (~(1<<30));
return 0;
}
/** Returns the current frame number. */
static int get_frame_number(struct usb_hcd *hcd)
{
hprt0_data_t hprt0;
dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED)
return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3;
else
return dwc_otg_hcd_get_frame_number(dwc_otg_hcd);
}
@@ -836,15 +871,16 @@ static int urb_enqueue(struct usb_hcd *hcd,
usb_maxpacket(urb->dev, urb->pipe,
!(usb_pipein(urb->pipe))));
if (hcd->self.uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) &&
!urb->transfer_buffer)
{
DWC_ERROR("dwc_otg_hcd: urb->transfer_buffer not set. Bailing out.\n");
DWC_FREE(dwc_otg_urb);
return -EINVAL;
}
buf = urb->transfer_buffer;
if (hcd->self.uses_dma) {
/*
* Calculate virtual address from physical address,
* because some class driver may not fill transfer_buffer.
* In Buffer DMA mode virual address is used,
* when handling non DWORD aligned buffers.
*/
buf = phys_to_virt(urb->transfer_dma);
}
if (!(urb->transfer_flags & URB_NO_INTERRUPT))
flags |= URB_GIVEBACK_ASAP;
@@ -919,6 +955,10 @@ static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
if(unlikely(retval))
goto EXIT;
#endif
if(usb_pipeint(urb->pipe) && (dwc_otg_hcd->ssplit_lock == usb_pipedevice(urb->pipe))){
DWC_DEBUGPL(DBG_HCD, "addr=%d(%p)\n",usb_pipedevice(urb->pipe),urb->hcpriv);
dwc_otg_hcd->ssplit_lock = 0;
}
if(urb->hcpriv == NULL){
DWC_WARN("urb->hcpriv == NULL! urb = %p status=%d\n",urb,status);
@@ -961,6 +1001,8 @@ static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
"endpoint=%d,is_intr=%d\n", ep->desc.bEndpointAddress,
dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress),
usb_endpoint_xfer_int(&ep->desc));
if(usb_endpoint_xfer_int(&ep->desc))
dwc_otg_hcd->ssplit_lock = 0;
dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250);
ep->hcpriv = NULL;
}
@@ -974,6 +1016,9 @@ static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
if(usb_endpoint_xfer_int(&ep->desc))
dwc_otg_hcd->ssplit_lock = 0;
if (ep->hcpriv) {
dwc_otg_hcd_endpoint_reset(dwc_otg_hcd, ep->hcpriv);

242
drivers/amlogic/usb/dwc_otg/310/dwc_otg_hcd_queue.c Executable file → Normal file
View File

@@ -42,8 +42,6 @@
#include "dwc_otg_hcd.h"
#include "dwc_otg_regs.h"
extern bool microframe_schedule;
/**
* Free each QTD in the QH's QTD-list then free the QH. QH should already be
* removed from a list. QTD list should already be empty if called from URB
@@ -155,6 +153,7 @@ static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, int bytecount)
* to initialize the QH.
*/
#define SCHEDULE_SLOP 10
#define SCHEDULE_SPLIT_SLOP 10
void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
{
char *speed, *type;
@@ -179,10 +178,6 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port);
qh->do_split = 0;
if (microframe_schedule)
qh->speed = dev_speed;
qh->nak_frame = 0xffff;
if (((dev_speed == USB_SPEED_LOW) ||
(dev_speed == USB_SPEED_FULL)) &&
@@ -192,7 +187,6 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
hub_port);
qh->do_split = 1;
qh->skip_count = 0;
}
if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
@@ -227,6 +221,10 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
qh->start_split_frame = qh->sched_frame;
}
}else if(qh->do_split){
qh->interval = SCHEDULE_SPLIT_SLOP;
qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
SCHEDULE_SPLIT_SLOP);
}
DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
@@ -319,8 +317,6 @@ dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
return qh;
}
/* microframe_schedule=0 start */
/**
* Checks that a channel is available for a periodic transfer.
*
@@ -389,162 +385,6 @@ static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
return status;
}
/* microframe_schedule=0 end */
/**
* Microframe scheduler
* track the total use in hcd->frame_usecs
* keep each qh use in qh->frame_usecs
* when surrendering the qh then donate the time back
*/
const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 };
/*
* called from dwc_otg_hcd.c:dwc_otg_hcd_init
*/
int init_hcd_usecs(dwc_otg_hcd_t *_hcd)
{
int i;
for (i=0; i<8; i++) {
_hcd->frame_usecs[i] = max_uframe_usecs[i];
}
return 0;
}
static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
{
int i;
unsigned short utime;
int t_left;
int ret;
int done;
ret = -1;
utime = _qh->usecs;
t_left = utime;
i = 0;
done = 0;
while (done == 0) {
/* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */
if (utime <= _hcd->frame_usecs[i]) {
_hcd->frame_usecs[i] -= utime;
_qh->frame_usecs[i] += utime;
t_left -= utime;
ret = i;
done = 1;
return ret;
} else {
i++;
if (i == 8) {
done = 1;
ret = -1;
}
}
}
return ret;
}
/*
* use this for FS apps that can span multiple uframes
*/
static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
{
int i;
int j;
unsigned short utime;
int t_left;
int ret;
int done;
unsigned short xtime;
ret = -1;
utime = _qh->usecs;
t_left = utime;
i = 0;
done = 0;
loop:
while (done == 0) {
if(_hcd->frame_usecs[i] <= 0) {
i++;
if (i == 8) {
done = 1;
ret = -1;
}
goto loop;
}
/*
* we need n consecutive slots
* so use j as a start slot j plus j+1 must be enough time (for now)
*/
xtime= _hcd->frame_usecs[i];
for (j = i+1 ; j < 8 ; j++ ) {
/*
* if we add this frame remaining time to xtime we may
* be OK, if not we need to test j for a complete frame
*/
if ((xtime+_hcd->frame_usecs[j]) < utime) {
if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) {
j = 8;
ret = -1;
continue;
}
}
if (xtime >= utime) {
ret = i;
j = 8; /* stop loop with a good value ret */
continue;
}
/* add the frame time to x time */
xtime += _hcd->frame_usecs[j];
/* we must have a fully available next frame or break */
if ((xtime < utime)
&& (_hcd->frame_usecs[j] == max_uframe_usecs[j])) {
ret = -1;
j = 8; /* stop loop with a bad value ret */
continue;
}
}
if (ret >= 0) {
t_left = utime;
for (j = i; (t_left>0) && (j < 8); j++ ) {
t_left -= _hcd->frame_usecs[j];
if ( t_left <= 0 ) {
_qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left;
_hcd->frame_usecs[j]= -t_left;
ret = i;
done = 1;
} else {
_qh->frame_usecs[j] += _hcd->frame_usecs[j];
_hcd->frame_usecs[j] = 0;
}
}
} else {
i++;
if (i == 8) {
done = 1;
ret = -1;
}
}
}
return ret;
}
static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
{
int ret;
ret = -1;
if (_qh->speed == USB_SPEED_HIGH) {
/* if this is a hs transaction we need a full frame */
ret = find_single_uframe(_hcd, _qh);
} else {
/* if this is a fs transaction we may need a sequence of frames */
ret = find_multi_uframe(_hcd, _qh);
}
return ret;
}
/**
* Checks that the max transfer size allowed in a host channel is large enough
* to handle the maximum data transfer in a single (micro)frame for a periodic
@@ -575,8 +415,6 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
return status;
}
/**
* Schedules an interrupt or isochronous transfer in the periodic schedule.
*
@@ -589,27 +427,8 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
{
int status = 0;
uint16_t frame_number;
if (microframe_schedule) {
int frame;
status = find_uframe(hcd, qh);
frame = -1;
if (status == 0) {
frame = 7;
} else {
if (status > 0 )
frame = status-1;
}
/* Set the new frame up */
if (frame > -1) {
qh->sched_frame &= ~0x7;
qh->sched_frame |= (frame & 7);
}
if (status != -1)
status = 0;
} else {
status = periodic_channel_available(hcd);
if (status) {
DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE
@@ -617,35 +436,39 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
}
status = check_periodic_bandwidth(hcd, qh);
}
if (status) {
DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); //NOTICE
return status;
}
status = check_max_xfer_size(hcd, qh);
if (status) {
DWC_INFO("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__); //NOTICE
return status;
}
frame_number = dwc_otg_hcd_get_frame_number(hcd);
if((qh->ep_type == UE_INTERRUPT) && !(qh->ep_is_in)){
//if(((frame_sched - frame_number) > _qh->interval) || dwc_frame_num_le(frame_number,DWC_HFNUM_MAX_FRNUM + frame_sched)){
qh->sched_frame = dwc_frame_num_inc(frame_number, SCHEDULE_SLOP);
//}//fix it in future
}
if((qh->ep_type == UE_INTERRUPT) && !qh->do_split &&
!dwc_frame_num_le(qh->sched_frame,dwc_frame_num_inc(frame_number,qh->interval)))
qh->sched_frame = dwc_frame_num_inc(frame_number, qh->interval);
if (hcd->core_if->dma_desc_enable) {
/* Don't rely on SOF and start in ready schedule */
DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
}
else {
if(fiq_enable &&hcd->core_if->use_fiq_flag && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame)))
{
hcd->fiq_state->next_sched_frame = qh->sched_frame;
}
/* Always start in the inactive schedule. */
DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
}
if (!microframe_schedule) {
/* Reserve the periodic channel. */
hcd->periodic_channels++;
}
/* Update claimed usecs per (micro)frame. */
hcd->periodic_usecs += qh->usecs;
@@ -653,7 +476,6 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
return status;
}
/**
* This function adds a QH to either the non periodic or periodic schedule if
* it is not already in the schedule. If the QH is already in the schedule, no
@@ -697,21 +519,13 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
*/
static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
{
int i;
DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
/* Release the periodic channel reservation. */
hcd->periodic_channels--;
/* Update claimed usecs per (micro)frame. */
hcd->periodic_usecs -= qh->usecs;
if (!microframe_schedule) {
/* Release the periodic channel reservation. */
hcd->periodic_channels--;
} else {
for (i = 0; i < 8; i++) {
hcd->frame_usecs[i] += qh->frame_usecs[i];
qh->frame_usecs[i] = 0;
}
}
}
/**
@@ -738,7 +552,7 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
} else {
deschedule_periodic(hcd, qh);
hcd->periodic_qh_count--;
if( !hcd->periodic_qh_count && !(fiq_fsm_enable &&hcd->core_if->use_fiq_flag)) {
if( !hcd->periodic_qh_count ) {
intr_mask.b.sofintr = 1;
DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk,
intr_mask.d32, 0);
@@ -818,16 +632,10 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
* Remove from periodic_sched_queued and move to
* appropriate queue.
*/
if ((microframe_schedule && dwc_frame_num_le(qh->sched_frame, frame_number)) ||
(!microframe_schedule && qh->sched_frame == frame_number)) {
if (qh->sched_frame == frame_number) {
DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
&qh->qh_list_entry);
} else {
if(fiq_enable && hcd->core_if->use_fiq_flag && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame))
{
hcd->fiq_state->next_sched_frame = qh->sched_frame;
}
DWC_LIST_MOVE_HEAD
(&hcd->periodic_sched_inactive,
&qh->qh_list_entry);
@@ -884,6 +692,7 @@ void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
/* Store the qtd ptr in the urb to reference what QTD. */
urb->qtd = qtd;
urb->qh_state = URB_STATE_IDLE;
return;
}
@@ -917,9 +726,6 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
if (*qh == NULL) {
retval = -1;
goto done;
} else {
if (fiq_enable && hcd->core_if->use_fiq_flag)
hcd->fiq_state->kick_np_queues = 1;
}
}
// DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd.c Executable file → Normal file
View File

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd.h Executable file → Normal file
View File

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_if.h Executable file → Normal file
View File

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c Executable file → Normal file
View File

View File

@@ -1282,7 +1282,7 @@ int pcd_init(
*/
DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", _dev->irq);
retval = request_irq(_dev->irq, dwc_otg_pcd_irq,
IRQF_SHARED | IRQF_DISABLED | IRQ_TYPE_LEVEL_HIGH,
IRQF_SHARED | IRQF_DISABLED,
gadget_wrapper->gadget.name, otg_dev->pcd);
if (retval != 0) {
DWC_ERROR("request of irq%d failed\n", _dev->irq);
@@ -1290,6 +1290,11 @@ int pcd_init(
return -EBUSY;
}
if (irq_set_affinity(_dev->irq, cpumask_of(3))) {
pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
_dev->irq, 3);
}
dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);
return retval;

0
drivers/amlogic/usb/dwc_otg/310/dwc_otg_regs.h Executable file → Normal file
View File

View File

@@ -47,9 +47,6 @@
#include <asm/exception.h>
#include <asm/smp_plat.h>
#ifdef CONFIG_MESON_GIC_FIQ
#include <mach/am_regs.h>
#endif
#include "irqchip.h"
union gic_base {
@@ -83,9 +80,6 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock);
*/
#define NR_GIC_CPU_IF 8
static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
#ifdef CONFIG_MESON_GIC_FIQ
static int gic_fiq_enable __read_mostly;
#endif
/*
* Supported arch specific GIC irq extension.
@@ -105,9 +99,6 @@ struct irq_chip gic_arch_extn = {
#endif
static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly;
#ifdef CONFIG_MESON_GIC_FIQ
static int gic_fiq_enable __read_mostly;
#endif
#ifdef CONFIG_GIC_NON_BANKED
static void __iomem *gic_get_percpu_base(union gic_base *base)
@@ -303,7 +294,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
handle_IRQ(irqnr, regs);
@@ -430,21 +421,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
#ifdef CONFIG_MESON_GIC_FIQ
if(gic_fiq_enable == 1)
writel_relaxed(3, base + GIC_DIST_CTRL);
else
writel_relaxed(1, base + GIC_DIST_CTRL);
#else
writel_relaxed(1, base + GIC_DIST_CTRL);
#endif
}
void gic_set_fiq_fake(unsigned fiq,irq_flow_handler_t handle)
{
if((fiq>=32) && (fiq<=1020)){
irq_set_chip_and_handler(fiq, &gic_chip, handle);
}
writel_relaxed(1, base + GIC_DIST_CTRL);
}
static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
@@ -453,20 +430,7 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
void __iomem *base = gic_data_cpu_base(gic);
unsigned int cpu_mask, cpu = smp_processor_id();
int i;
#ifdef CONFIG_MESON_GIC_FIQ
/*********************************************
* suspend/resume need save&restore GIC FIQ setting
*********************************************/
unsigned int it_lines_number;
unsigned cmd;
char c=0;
unsigned temp;
if(gic_fiq_enable == 1){
cmd = readl_relaxed((const volatile void *)P_AO_RTI_STATUS_REG1);
c = (char)cmd;
}
#endif
/*
* Get what the GIC says our CPU mask is.
*/
@@ -489,51 +453,14 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
#ifdef CONFIG_MESON_GIC_FIQ
if(gic_fiq_enable == 1){
/* Set the Per-CPU interrupts 15-8 as Secure and the rest
* as Non-secure */
it_lines_number = readl_relaxed(dist_base + GIC_DIST_CTR) & 0x1f;
writel_relaxed(0xffff00ff, dist_base + GIC_DIST_IGROUP);
if(c != 'r'){
/* Set ALL interrupts as non-secure interrupts */
for(i = 1; i <= it_lines_number; i++) {
temp = readl_relaxed(dist_base + GIC_DIST_IGROUP + i * 4);
if(temp == 0)
writel_relaxed(0xffffffff, dist_base + GIC_DIST_IGROUP + i * 4);
}
}
/*
* Set priority on PPI and SGI interrupts
*/
for (i = 0; i < 32; i += 4){
temp = readl_relaxed(dist_base + GIC_DIST_PRI + i * 4 / 4);
if(temp == 0)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
}
}
else{
for (i = 0; i < 32; i += 4)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
}
#else
/*
* Set priority on PPI and SGI interrupts
*/
for (i = 0; i < 32; i += 4)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
#endif
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
#ifdef CONFIG_MESON_GIC_FIQ
if(gic_fiq_enable == 1)
writel_relaxed(0xf, base + GIC_CPU_CTRL); //??GICC_ACTR maybe set 0
else
writel_relaxed(1, base + GIC_CPU_CTRL);
#else
writel_relaxed(1, base + GIC_CPU_CTRL);
#endif
}
#ifdef CONFIG_CPU_PM
@@ -611,14 +538,7 @@ static void gic_dist_restore(unsigned int gic_nr)
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
dist_base + GIC_DIST_ENABLE_SET + i * 4);
#ifdef CONFIG_MESON_GIC_FIQ
if(gic_fiq_enable == 1)
writel_relaxed(3, dist_base + GIC_DIST_CTRL);
else
writel_relaxed(1, dist_base + GIC_DIST_CTRL);
#else
writel_relaxed(1, dist_base + GIC_DIST_CTRL);
#endif
}
static void gic_cpu_save(unsigned int gic_nr)
@@ -675,14 +595,7 @@ static void gic_cpu_restore(unsigned int gic_nr)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
#ifdef CONFIG_MESON_GIC_FIQ
if(gic_fiq_enable == 1)
writel_relaxed(0xf, cpu_base + GIC_CPU_CTRL);
else
writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
#else
writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
#endif
}
static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
@@ -756,14 +669,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
dsb();
/* this always happens on GIC0 */
#ifdef CONFIG_MESON_GIC_FIQ
if(gic_fiq_enable == 1)
writel_relaxed(map << 16 | 1<<15 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
else
writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
#else
writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
#endif
}
#endif
@@ -948,13 +854,6 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)
if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
percpu_offset = 0;
#ifdef CONFIG_MESON_GIC_FIQ
if(of_property_read_bool(node, "gic_fiq_enable"))
gic_fiq_enable=1;
else
gic_fiq_enable = 0;
#endif
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
@@ -971,11 +870,4 @@ IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
#ifdef CONFIG_MESON_GIC_FIQ
int get_gic_fiq_enable_flag(void)
{
return gic_fiq_enable;
}
#endif
#endif

4
kernel/irq/chip.c Executable file → Normal file
View File

@@ -15,7 +15,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <asm/fiq.h>
#include <trace/events/irq.h>
#include "internals.h"
@@ -454,7 +454,7 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
goto out;
}
if ((irq!=get_fiq_index()) && (desc->istate & IRQS_ONESHOT))
if (desc->istate & IRQS_ONESHOT)
mask_irq(desc);
preflow_handler(desc);