/*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #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 ur8_vlynq_startup_link(volatile struct _vlynq_registers *V, unsigned int vlynq_clock, unsigned int reset_bit) { unsigned int count, clk, divisor; printk("[ur8_vlynq_startup_link] \n"); /*----------------------------------------------------------------------------------*\ * device aus den reset holen \*----------------------------------------------------------------------------------*/ if(reset_bit != (unsigned int)-1) ur8_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 = ur8_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 < UR8_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; ur8_put_device_into_reset(reset_bit); printk(KERN_ERR "Error: vlynq link could not be established\n"); return 1; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_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; default: return 1; } /*Schicke VlynqController in den Reset*/ ur8_put_device_into_reset(reset_bit); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_vlynq_init(void) { unsigned int i; unsigned int reset_bit; unsigned int int_nr, ret; /*--- unsigned char *mac = "\x00\x04\x0e\x12\x34\x56"; ---*/ #if 0 /*--- zum testen des WLAN-Modul aus dem reset holen ---*/ ur8_gpio_ctrl(GPIO_BIT_EPHY_FDUPLEX, GPIO_PIN, GPIO_OUTPUT_PIN); /*--- GPIO 17 ist Reset für WLAN ---*/ ur8_gpio_out_bit(GPIO_BIT_EPHY_FDUPLEX, 1); #endif for (i = 0 ; i < MAX_VLYNQ_DEVICES ; i++) { /* Wurde Speicher für die Instance angelegt? */ if ( vlynq_config[i] == NULL ) return 1; /* Wurde die Vlynqinstanze bereits initialisiert? */ if( vlynq_config[i]->vlynq==NULL) { printk("[ur8_vlynq_init] device %u\n", i); switch(i) { case 0: vlynq_config[i]->vlynq = (volatile struct _vlynq_registers *)UR8_VLYNQ0_CTRL_BASE; reset_bit = VLYNQ0_RESET_BIT; int_nr = UR8INT_VLYNQ0; break; default: continue; } if(ur8_vlynq_startup_link(vlynq_config[i]->vlynq, CONFIG_VLYNQ_SUPPORT_CLK, reset_bit)) { return 2; } /*--- register vlynq interrupt ---*/ ret = request_irq(int_nr, vlynq_interrupt, SA_INTERRUPT, "VLYNQ", vlynq_config[i]); if (ret) { printk(KERN_INFO "[ur8_vlynq_init] request_irq %d failed, reason %d\n", int_nr, ret); return ret; } } } return 0; /* success */ } #if 0 /*--- zum testen ---*/ late_initcall(ur8_vlynq_init); #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_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 ur8_vlynq_config_interrupt(volatile struct _vlynq_registers_half *V, volatile struct _vlynq_interrupts *Irq) { unsigned int val; if(Irq->LocalEnable == VLYNQ_INT_OFF) { return 0; } /* 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(0, 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); V->Control.Register = (V->Control.Register & ~((1 << VLYNQ_CTL_INT2CFG_SHIFT) || (1 << VLYNQ_CTL_INTLOCAL_SHIFT) || (1 << VLYNQ_CTL_INTEN_SHIFT))) | val; DEBUG_VLYNQ("Set Interr.-Ctrl = 0x%08x\n",val); DEBUG_VLYNQ("ControlReg (0x%08x) = 0x%08x\n",&V->Control, V->Control.Register); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_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 = ur8_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 = ur8_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 = ur8_vlynq_config_interrupt(&(V->local), &(context->LocalIrq)); if(ret) { return ret; } DEBUG_VLYNQ("REMOTE INTERRUPT aktivieren\n"); ret = ur8_vlynq_config_interrupt(&(V->remote), &(context->RemoteIrq)); if(ret) { return ret; } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ DEBUG_VLYNQ("Startup Vlynq Link\n"); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _vlynq_context *ur8_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 ur8_vlynq_free_context(unsigned int instance) { int int_nr; /* Interrupt fuer*/ if (instance >= MAX_VLYNQ_DEVICES) return 1; switch(instance) { case 0: int_nr = UR8INT_VLYNQ0; break; } free_irq(int_nr, vlynq_config[instance]); ur8_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(ur8_vlynq_init); EXPORT_SYMBOL(ur8_vlynq_init_link); EXPORT_SYMBOL( ur8_vlynq_free_context ); EXPORT_SYMBOL(ur8_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);