/*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUGVLYNQ #define DEBUG_VLYNQ(args...) printk(KERN_INFO "###VLYNQ###: " args) #else #define DEBUG_VLYNQ(args...) /* args */ #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _vlynq_context *vlynq_config[MAX_VLYNQ_DEVICES]; int avm_vlync_double_speed = 0; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ar7_vlynq_startup_link(volatile struct _vlynq_registers *V, unsigned int vlynq_clock, unsigned int reset_bit) { unsigned int count, clk, divisor; printk("[ar7_vlynq_startup_link] \n"); /*----------------------------------------------------------------------------------*\ * device aus den reset holen \*----------------------------------------------------------------------------------*/ if(reset_bit != (unsigned int)-1) ar7_reset_device(reset_bit, 10); V->local.Control.Bits.reset = 1; ndelay(100); V->local.Control.Register = 0; /*----------------------------------------------------------------------------------*\ * clock divider einstellen \*----------------------------------------------------------------------------------*/ if(avm_vlync_double_speed) { printk("[speedup]: vlynq_clock(%u) -> vlynq_clock(%u)\n", vlynq_clock, vlynq_clock << 1); vlynq_clock <<= 1; } clk = ar7_get_clock(avm_clock_id_system); divisor = clk / vlynq_clock; if(clk / divisor != vlynq_clock) { printk(KERN_WARNING "Warning: configurated vlynq clock not reached (config %uHz) set %uHz\n", vlynq_clock, clk / divisor); } V->local.Control.Bits.clkdir = 1; /* 1 clk from divisor */ V->local.Control.Bits.clkdiv = divisor-1; /* clkdiv + 1 = divisor*/ /*----------------------------------------------------------------------------------*\ * pruefen ob der Link vorhanden ist \*----------------------------------------------------------------------------------*/ for(count = 0 ; count < AR7_VLYNQ_INIT_RETRY ; count++) { udelay(64); /*--- 64 uSekunden warten ----*/ if(V->local.Status.Bits.link) break; } if(V->local.Status.Bits.link == 0) { V->local.Control.Bits.reset = 1; ar7_put_device_into_reset(reset_bit); printk(KERN_ERR "Error: vlynq link could not be established\n"); return 1; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ar7_vlynq_release_link(unsigned int instance) { unsigned int reset_bit; if ( instance >= MAX_VLYNQ_DEVICES ) return 1; switch ( instance ) { case 0: reset_bit=VLYNQ0_RESET_BIT; break; case 1: reset_bit=VLYNQ1_RESET_BIT; break; default: return 1; } /*--- Schicke VlynqController in den Reset ---*/ ar7_put_device_into_reset(reset_bit); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ar7_vlynq_init(unsigned int instance) { unsigned int reset_bit; unsigned int int_nr, ret; DEBUG_VLYNQ("[ar7_vlynq_init] MAX_VLYNQ_DEVICES %d\n",MAX_VLYNQ_DEVICES); printk("[ar7_vlynq_init] device %u\n", instance); /* Wurde Speicher für die Instance angelegt? */ if ( vlynq_config[instance] == NULL ) { DEBUG_VLYNQ("[ar7_vlynq_init] vlynq_config[%d] == NULL\n", instance); return 1; } /* Wurde die Vlynqinstanze bereits initialisiert? */ if( vlynq_config[instance]->vlynq==NULL) { switch(instance) { case 0: vlynq_config[instance]->vlynq = (volatile struct _vlynq_registers *)AR7_VLYNQ0_CTRL_BASE; reset_bit = VLYNQ0_RESET_BIT; int_nr = AR7INT_VLYNQ0; break; case 1: vlynq_config[instance]->vlynq = (volatile struct _vlynq_registers *)AR7_VLYNQ1_CTRL_BASE; reset_bit = VLYNQ1_RESET_BIT; int_nr = AR7INT_VLYNQ1; break; default: return 3; } printk("[ar7_vlynq_init] device %d int_nr %d rest_bit %d CTRL_BASE 0x%x\n", instance, int_nr, reset_bit, vlynq_config[instance]->vlynq); if(ar7_vlynq_startup_link(vlynq_config[instance]->vlynq, CONFIG_VLYNQ_SUPPORT_CLK, reset_bit)) { return 2; } /*--- register vlynq interrupt ---*/ ret = request_irq(int_nr, vlynq_interrupt, SA_INTERRUPT, instance?"VLYNQ1":"VLYNQ0", vlynq_config[instance]); if (ret) { printk(KERN_INFO "[ar7_vlynq_init] request_irq %d failed, reason %d\n", int_nr, ret); return ret; } } return 0; /* success */ } #if 0 /*--- zum testen ---*/ late_initcall(ar7_vlynq_init); #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ar7_vlynq_config_memory_windows(volatile struct _vlynq_registers_half *VL, volatile struct _vlynq_registers_half *VR, struct ___vlynq_Rx_Address LocalWindows[], unsigned int LocalWindowCount, unsigned int RemoteTxAddr) { unsigned int i; if(LocalWindowCount > 4) { printk(KERN_ERR "ERROR: vlynq: maximal 4 windows allowed\n"); return 1; } VR->Tx_Address = RemoteTxAddr; DEBUG_VLYNQ("Tx_Address=0x%.8x\n", RemoteTxAddr); for(i = 0 ; i < LocalWindowCount ; i++) { VL->Rx_Address[i] = LocalWindows[i]; DEBUG_VLYNQ("Rx_Address=0x%.8x\n", LocalWindows[i].Offset); DEBUG_VLYNQ("Rx_Size=0x%.8x\n\n", LocalWindows[i].Size); } for( ; i < 4 ; i++) { VL->Rx_Address[i].Size = 0; VL->Rx_Address[i].Offset = 0; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ar7_vlynq_config_interrupt(unsigned int instance, volatile struct _vlynq_registers_half *V, volatile struct _vlynq_interrupts *Irq) { unsigned int val; if(Irq->LocalEnable == VLYNQ_INT_OFF) { return 0; } /* ==20070805 SK== * Vlynq status interrupts are registered and enabled although any handling is missing. * We need to register another irq routine (irq number = Vlynq virtual irq plus status * interrupt vector --> Irq->irq) to process status interrupts or we can disable status * interrupts. * !To distinguish between status interrupts and (remote) device interrupts different * vectors have to be assigned. Mind the note concerning interrupt priority status * register as well. */ /* Map local module status interrupts to interrupt vector*/ val = ((Irq->vector) & 0x1F) << VLYNQ_CTL_INTVEC_SHIFT ; /*--- set vector to virtual interrupt ---*/ Irq->irq = vlynq_vector_to_irq(instance, Irq->vector); /* enable local module status interrupts */ val |= 1 << VLYNQ_CTL_INTEN_SHIFT; /*--- enable später ---*/ if (Irq->LocalEnable == VLYNQ_INT_LOCAL ) { /*set the intLocal bit*/ val |= 1 << VLYNQ_CTL_INTLOCAL_SHIFT; } /* Irrespective of whether interrupts are handled locally, program * int2Cfg. Error checking for accidental loop(when intLocal=0 and int2Cfg=1 * i.e remote packets are set intPending register->which will result in * same packet being sent out) has been done already */ if (Irq->RemoteEnable == VLYNQ_INT_ROOT_ISR) { /* Set the int2Cfg register, so that remote interrupt * packets are written to intPending register */ val |= 1 << VLYNQ_CTL_INT2CFG_SHIFT; /* Set intPtr register to point to intPending register */ V->Interrupt_Pointer = ((unsigned int)&(V->Interrupt_Pending_Set) & 0xFF); } else { /*set the interrupt pointer register*/ /*--- VL->Interrupt_Pointer = local interrupt pointer adresse ---*/ /*--- V->Interrupt_Pointer = TODO ---*/ panic("set interrupt pointer register to Interruptcontroller not implemented\n"); val &= ~(1 << VLYNQ_CTL_INT2CFG_SHIFT); } DEBUG_VLYNQ("ControlReg (0x%08x) = 0x%08x\n",&V->Control, V->Control.Register); DEBUG_VLYNQ("Set Interr.-Ctrl = 0x%08x\n",val); V->Control.Register = (V->Control.Register & ~((1 << VLYNQ_CTL_INT2CFG_SHIFT) || (1 << VLYNQ_CTL_INTLOCAL_SHIFT) || (1 << VLYNQ_CTL_INTEN_SHIFT))) | val; DEBUG_VLYNQ("ControlReg (0x%08x) = 0x%08x\n",&V->Control, V->Control.Register); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ar7_vlynq_init_link(unsigned int instance) { unsigned int ret; volatile struct _vlynq_registers *V = NULL; struct _vlynq_context* context=NULL; if( instance >= MAX_VLYNQ_DEVICES ) return 1; context=vlynq_config[instance]; if(context==NULL) return 2; V = context->vlynq; DEBUG_VLYNQ("Remote => Local\n"); ret = ar7_vlynq_config_memory_windows(&(V->local), &(V->remote), context->Remote2Local.Windows, context->Remote2Local.WindowCount, context->Remote2Local.Tx_Address); if(ret) return ret; DEBUG_VLYNQ("Local => Remote\n"); ret = ar7_vlynq_config_memory_windows(&(V->remote), &(V->local), context->Local2Remote.Windows, context->Local2Remote.WindowCount, context->Local2Remote.Tx_Address); if(ret) return ret; /*--------------------------------------------------------------------------------------*\ * ggf. noch diverse Parameter setzen \*--------------------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------------------*\ * Configure Interrupt Priority Vector Status/Clear Register \*--------------------------------------------------------------------------------------*/ #warning "Sascha: Sollte in eine eigene Funktion ausgelagert\n" V->local.Interrupt_Priority.Bits.intstat=0x7; V->local.Interrupt_Priority.Bits.nointpend=0; /*--------------------------------------------------------------------------------------*\ * Interrupt aktivieren \*--------------------------------------------------------------------------------------*/ DEBUG_VLYNQ("LOCAL INTERRUPT aktivieren\n"); ret = ar7_vlynq_config_interrupt(instance, &(V->local), &(context->LocalIrq)); if(ret) { return ret; } DEBUG_VLYNQ("REMOTE INTERRUPT aktivieren\n"); ret = ar7_vlynq_config_interrupt(instance, &(V->remote), &(context->RemoteIrq)); if(ret) { return ret; } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ DEBUG_VLYNQ("Startup Vlynq Link\n"); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _vlynq_context *ar7_vlynq_alloc_context(unsigned int instance) { struct _vlynq_context *C; if(instance >= MAX_VLYNQ_DEVICES) return 0; C = kmalloc(sizeof(struct _vlynq_context), GFP_ATOMIC); if(C) { memset(C, 0, sizeof(*C)); C->instance = instance; vlynq_config[instance]=C; } return C; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ar7_vlynq_free_context(unsigned int instance) { int int_nr = 0; /* Interrupt fuer*/ if (instance >= MAX_VLYNQ_DEVICES) return 1; switch(instance) { case 0: int_nr = AR7INT_VLYNQ0; break; case 1: int_nr = AR7INT_VLYNQ1; break; default: return 1; } free_irq(int_nr, vlynq_config[instance]); ar7_vlynq_release_link( instance ); kfree(vlynq_config[instance]); vlynq_config[instance]=NULL; return 0; } /*------------------------------------------------------------------------------------------*\ V->local.Control.Bits.reset = 0; V->local.Control.Bits.iloop = 0; V->local.Control.Bits.aopt_disable = 0; V->local.Control.Bits.int2cfg = 1: V->local.Control.Bits.intvec = 0; / * 5-Bit Vector * / V->local.Control.Bits.intenable = 0; / * 1 enable, 0 disable * / V->local.Control.Bits.intlocal = 1; / * 1 local, 0 remote * / V->local.Control.Bits.clkdir = 1; / * 1 clk from divisor * / V->local.Control.Bits.clkdiv = divisor; V->local.Control.Bits.txfastpath = 0; / * buffered or not * / V->local.Control.Bits.rtmenable = 0; V->local.Control.Bits.rtmvalidwr = 0; V->local.Control.Bits.rxsampleval = 0; V->local.Control.Bits.sclkpudis = V->local.Control.Bits.pmen = \*------------------------------------------------------------------------------------------*/ /* =============== EXPORT SYMBOLS ============= */ EXPORT_SYMBOL(vlynq_get_irq); EXPORT_SYMBOL(vlynq_irq_vector_setup); EXPORT_SYMBOL(ar7_vlynq_init); EXPORT_SYMBOL(ar7_vlynq_init_link); EXPORT_SYMBOL(ar7_vlynq_free_context); EXPORT_SYMBOL(ar7_vlynq_alloc_context); EXPORT_SYMBOL(vlynq_vector_to_irq); EXPORT_SYMBOL(vlynq_interrupt); EXPORT_SYMBOL(vlynq_irq_status); EXPORT_SYMBOL(vlynq_irq_enable_mask); EXPORT_SYMBOL(vlynq_irq_enable); EXPORT_SYMBOL(vlynq_irq_disable); EXPORT_SYMBOL(vlynq_irq_ack);