From 83b991afff40e12a8b6756af06a472842edb1a66 Mon Sep 17 00:00:00 2001 From: Eric Biederman Date: Sat, 11 Oct 2003 06:20:25 +0000 Subject: - O2, enums, and switch statements work in romcc - Support for compiling romcc on non x86 platforms - new romc options -msse and -mmmx for specifying extra registers to use - Bug fixes to device the device disable/enable framework and an amd8111 implementation - Move the link specification to the chip specification instead of the path - Allow specifying devices with internal bridges. - Initial via epia support - Opteron errata fixes git-svn-id: svn://svn.coreboot.org/coreboot/trunk@1200 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- src/northbridge/amd/amdk8/Config.lb | 1 + src/northbridge/amd/amdk8/coherent_ht.c | 124 ++++++------ src/northbridge/amd/amdk8/cpu_rev.c | 25 +++ src/northbridge/amd/amdk8/misc_control.c | 42 ++++ src/northbridge/amd/amdk8/northbridge.c | 2 +- src/northbridge/amd/amdk8/raminit.c | 320 +++++++++++++++++++++++++------ 6 files changed, 400 insertions(+), 114 deletions(-) create mode 100644 src/northbridge/amd/amdk8/cpu_rev.c (limited to 'src/northbridge/amd/amdk8') diff --git a/src/northbridge/amd/amdk8/Config.lb b/src/northbridge/amd/amdk8/Config.lb index 6112ed18c8..0110e218ce 100644 --- a/src/northbridge/amd/amdk8/Config.lb +++ b/src/northbridge/amd/amdk8/Config.lb @@ -1,3 +1,4 @@ config chip.h object northbridge.o driver misc_control.o +driver mcf0_control.o diff --git a/src/northbridge/amd/amdk8/coherent_ht.c b/src/northbridge/amd/amdk8/coherent_ht.c index b2839159c7..f05ee3d22d 100644 --- a/src/northbridge/amd/amdk8/coherent_ht.c +++ b/src/northbridge/amd/amdk8/coherent_ht.c @@ -100,43 +100,6 @@ static void disable_probes(void) print_debug("done.\r\n"); } -//BY LYH -#define WAIT_TIMES 1000 -static void wait_ap_stop(u8 node) -{ - unsigned long reg; - unsigned long i; - for(i=0;i>= 24; -#if 0 - print_debug("applicaton cpu apic_id: "); - print_debug_hex32(apic_id); - print_debug("\r\n"); -#endif - /* AP apic_id == node_id ? */ - if(apic_id != 0) { - /* set the ColdResetbit to notify BSP that AP is stopped */ - reg = pci_read_config32(NODE_HT(apic_id), 0x6C); - reg |= 1<<4; - pci_write_config32(NODE_HT(apic_id), 0x6C, reg); - } - -} -//BY LYH END static void enable_routing(u8 node) { @@ -169,15 +132,8 @@ static void enable_routing(u8 node) print_debug_hex32(node); val=pci_read_config32(NODE_HT(node), 0x6c); - val &= ~((1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)); - pci_write_config32(NODE_HT(node), 0x6c, val); -//BY LYH -#if 1 - if(node!=0) { - wait_ap_stop(node); - } -#endif -//BY LYH END + val &= ~((1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)); + pci_write_config32(NODE_HT(node), 0x6c, val); print_debug(" done.\r\n"); } @@ -225,6 +181,62 @@ static bool check_connection(u8 src, u8 dest, u8 link) return 1; } +static void optimize_connection(u8 node1, u8 link1, u8 node2, u8 link2) +{ + static const uint8_t link_width_to_pow2[]= { 3, 4, 0, 5, 1, 2, 0, 0 }; + static const uint8_t pow2_to_link_width[] = { 0x7, 4, 5, 0, 1, 3 }; + uint16_t freq_cap1, freq_cap2, freq_cap, freq_mask; + uint8_t width_cap1, width_cap2, width_cap, width, ln_width1, ln_width2; + uint8_t freq; + /* Set link width and frequency */ + + /* Get the frequency capabilities */ + freq_cap1 = pci_read_config16(NODE_HT(node1), 0x80 + link1 + PCI_HT_CAP_HOST_FREQ_CAP); + freq_cap2 = pci_read_config16(NODE_HT(node2), 0x80 + link2 + PCI_HT_CAP_HOST_FREQ_CAP); + + /* Calculate the highest possible frequency */ +#if 1 + /* FIXME!!!!!!! + * This method of computing the fastes frequency is broken. + * Because the frequencies (i.e. 100Mhz) are not ordered. + */ + freq = log2(freq_cap1 & freq_cap2 & 0xff); +#else + /* Only allow supported frequencies 800Mhz and below */ + freq = log2(freq_cap1 & freq_cap2 & 0x3f); +#endif + + /* Set the Calulcated link frequency */ + pci_write_config8(NODE_HT(node1), 0x80 + link1 + PCI_HT_CAP_HOST_FREQ, freq); + pci_write_config8(NODE_HT(node2), 0x80 + link2 + PCI_HT_CAP_HOST_FREQ, freq); + + /* Get the width capabilities */ + width_cap1 = pci_read_config8(NODE_HT(node1), 0x80 + link1 + PCI_HT_CAP_HOST_WIDTH); + width_cap2 = pci_read_config8(NODE_HT(node2), 0x80 + link2 + PCI_HT_CAP_HOST_WIDTH); + + /* Calculate node1's input width */ + ln_width1 = link_width_to_pow2[width_cap1 & 7]; + ln_width2 = link_width_to_pow2[(width_cap2 >> 4) & 7]; + if (ln_width1 > ln_width2) { + ln_width1 = ln_width2; + } + width = pow2_to_link_width[ln_width1]; + /* Calculate node1's output width */ + ln_width1 = link_width_to_pow2[(width_cap1 >> 4) & 7]; + ln_width2 = link_width_to_pow2[width_cap2 & 7]; + if (ln_width1 > ln_width2) { + ln_width1 = ln_width2; + } + width |= pow2_to_link_width[ln_width1] << 4; + + /* Set node1's widths */ + pci_write_config8(NODE_HT(node1), 0x80 + link1 + PCI_HT_CAP_HOST_WIDTH + 1, width); + + /* Set node2's widths */ + width = ((width & 0x70) >> 4) | ((width & 0x7) << 4); + pci_write_config8(NODE_HT(node2), 0x80 + link2 + PCI_HT_CAP_HOST_WIDTH + 1, width); +} + static void fill_row(u8 node, u8 row, u32 value) { #if 0 @@ -346,6 +358,7 @@ static u8 setup_smp(void) } /* We found 2 nodes so far */ + optimize_connection(0, ACROSS, 7, ACROSS); setup_node(0, cpus); /* Node 1 is there. Setup Node 0 correctly */ setup_remote_node(1, cpus); /* Setup the routes on the remote node */ rename_temp_node(1); /* Rename Node 7 to Node 1 */ @@ -444,17 +457,6 @@ static unsigned detect_mp_capabilities(unsigned cpus) #endif -/* this is a shrunken cpuid. */ - -static unsigned int cpuid(unsigned int op) -{ - unsigned int ret; - - asm volatile ( "cpuid" : "=a" (ret) : "a" (op)); - - return ret; -} - static void coherent_ht_finalize(unsigned cpus) { int node; @@ -469,7 +471,7 @@ static void coherent_ht_finalize(unsigned cpus) #if 1 print_debug("coherent_ht_finalize\r\n"); #endif - rev_a0=((cpuid(1)&0xffff)==0x0f10); + rev_a0= is_cpu_rev_a0(); for (node=0; node0f00c800 BY LYH +#endif pci_write_config32(NODE_HT(node),0x68,val); if (rev_a0) { @@ -508,7 +514,7 @@ static int setup_coherent_ht_domain(void) #endif coherent_ht_finalize(cpus); - /* this should probably go away again. */ + /* FIXME this should probably go away again. */ coherent_ht_mainboard(cpus); return reset_needed; } diff --git a/src/northbridge/amd/amdk8/cpu_rev.c b/src/northbridge/amd/amdk8/cpu_rev.c new file mode 100644 index 0000000000..51f235905e --- /dev/null +++ b/src/northbridge/amd/amdk8/cpu_rev.c @@ -0,0 +1,25 @@ +/* this is a shrunken cpuid. */ + +static unsigned int cpuid(unsigned int op) +{ + unsigned int ret; + unsigned dummy2,dummy3,dummy4; + + asm volatile ( + "cpuid" + : "=a" (ret), "=b" (dummy2), "=c" (dummy3), "=d" (dummy4) + : "a" (op) + ); + + return ret; +} + +static int is_cpu_rev_a0(void) +{ + return (cpuid(1) & 0xffff) == 0x0f10; +} + +static int is_cpu_pre_c0(void) +{ + return (cpuid(1) & 0xffef) < 0x0f48; +} diff --git a/src/northbridge/amd/amdk8/misc_control.c b/src/northbridge/amd/amdk8/misc_control.c index 639e34fbaf..045f5cef06 100644 --- a/src/northbridge/amd/amdk8/misc_control.c +++ b/src/northbridge/amd/amdk8/misc_control.c @@ -8,6 +8,7 @@ #include #include #include +#include "./cpu_rev.c" static void misc_control_init(struct device *dev) { @@ -17,7 +18,48 @@ static void misc_control_init(struct device *dev) cmd = pci_read_config32(dev, 0x44); cmd |= (1<<6) | (1<<25); pci_write_config32(dev, 0x44, cmd ); + if (is_cpu_pre_c0()) { + /* errata 58 */ + cmd = pci_read_config32(dev, 0x80); + cmd &= ~(1<<0); + pci_write_config32(dev, 0x80, cmd ); + cmd = pci_read_config32(dev, 0x84); + cmd &= ~(1<<24); + cmd &= ~(1<<8); + pci_write_config32(dev, 0x84, cmd ); + /* errata 66 */ + cmd = pci_read_config32(dev, 0x70); + cmd &= ~(1<<0); + cmd |= (1<<1); + pci_write_config32(dev, 0x70, cmd ); + cmd = pci_read_config32(dev, 0x7c); + cmd &= ~(3<<4); + pci_write_config32(dev, 0x7c, cmd ); + } + else { + /* errata 98 */ +#if 0 + cmd = pci_read_config32(dev, 0xd4); + if(cmd != 0x04e20707) { + cmd = 0x04e20707; + pci_write_config32(dev, 0xd4, cmd ); + hard_reset(); + } +#endif + cmd = 0x04e20707; + pci_write_config32(dev, 0xd4, cmd ); + } +#if 1 + cmd = pci_read_config32(dev, 0xdc); + if((cmd & 0x0000ff00) != 0x02500) { + cmd &= 0xffff00ff; + cmd |= 0x00002500; + pci_write_config32(dev, 0xdc, cmd ); + printk_debug("resetting cpu\n"); + hard_reset(); + } +#endif printk_debug("done.\n"); } diff --git a/src/northbridge/amd/amdk8/northbridge.c b/src/northbridge/amd/amdk8/northbridge.c index 7bcb1567ba..13845c5a70 100644 --- a/src/northbridge/amd/amdk8/northbridge.c +++ b/src/northbridge/amd/amdk8/northbridge.c @@ -475,6 +475,6 @@ static void enumerate(struct chip *chip) } struct chip_control northbridge_amd_amdk8_control = { - .enumerate = enumerate, .name = "AMD K8 Northbridge", + .enumerate = enumerate, }; diff --git a/src/northbridge/amd/amdk8/raminit.c b/src/northbridge/amd/amdk8/raminit.c index 802e4318b4..437ef2655d 100644 --- a/src/northbridge/amd/amdk8/raminit.c +++ b/src/northbridge/amd/amdk8/raminit.c @@ -124,6 +124,9 @@ #define DCH_MEMCLK_EN3 (1 << 29) /* Function 3 */ +#define MCA_NB_CONFIG 0x44 +#define MNC_ECC_EN (1 << 22) +#define MNC_CHIPKILL_EN (1 << 23) #define SCRUB_CONTROL 0x58 #define SCRUB_NONE 0 #define SCRUB_40ns 1 @@ -1127,23 +1130,6 @@ static void spd_set_ram_size(const struct mem_controller *ctrl) } } -//BY LYH //Fill next base reg with right value -static void fill_last(unsigned long node_id,unsigned long base) -{ - unsigned i; - unsigned base_reg; - base &=0xffff0000; - device_t device; - for(device = PCI_DEV(0, 0x18, 1); device <= PCI_DEV(0, 0x1f, 1); device -+= PCI_DEV(0, 1, 0)) { - for(i=node_id+1;i<=7;i++) { - base_reg=0x40+(i<<3); - pci_write_config32(device,base_reg,base); - } - } -} -//BY LYH END - static void route_dram_accesses(const struct mem_controller *ctrl, unsigned long base_k, unsigned long limit_k) { @@ -1177,7 +1163,12 @@ static void set_top_mem(unsigned tom_k) { /* Error if I don't have memory */ if (!tom_k) { - die("No memory"); + set_bios_reset(); + print_debug("No memory - reset"); + /* enable cf9 */ + pci_write_config8(PCI_DEV(0, 0x04, 3), 0x41, 0xf1); + /* reset */ + outb(0x0e, 0x0cf9); } #if 1 @@ -1204,15 +1195,102 @@ static void set_top_mem(unsigned tom_k) wrmsr(TOP_MEM, msr); } -static void order_dimms(const struct mem_controller *ctrl) +static unsigned long interleave_chip_selects(const struct mem_controller *ctrl) { - unsigned long tom, tom_k, base_k; - unsigned node_id; + /* 35 - 25 */ + static const uint32_t csbase_low[] = { + /* 32MB */ (1 << (13 - 4)), + /* 64MB */ (1 << (14 - 4)), + /* 128MB */ (1 << (14 - 4)), + /* 256MB */ (1 << (15 - 4)), + /* 512MB */ (1 << (15 - 4)), + /* 1GB */ (1 << (16 - 4)), + /* 2GB */ (1 << (16 - 4)), + }; + uint32_t csbase_inc; + int chip_selects, index; + int bits; + int dual_channel; + unsigned common_size; + uint32_t csbase, csmask; + + /* See if all of the memory chip selects are the same size + * and if so count them. + */ + chip_selects = 0; + common_size = 0; + for(index = 0; index < 8; index++) { + unsigned size; + uint32_t value; + + value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2)); + + /* Is it enabled? */ + if (!(value & 1)) { + continue; + } + chip_selects++; + size = value >> 21; + if (common_size == 0) { + common_size = size; + } + /* The size differed fail */ + if (common_size != size) { + return 0; + } + } + /* Chip selects can only be interleaved when there is + * more than one and their is a power of two of them. + */ + bits = log2(chip_selects); + if (((1 << bits) != chip_selects) || (bits < 1) || (bits > 3)) { + return 0; + + } + /* Also we run out of address mask bits if we try and interleave 8 4GB dimms */ + if ((bits == 3) && (common_size == (1 << (32 - 3)))) { + print_debug("8 4GB chip selects cannot be interleaved\r\n"); + return 0; + } + /* Find the bits of csbase that we need to interleave on */ + if (is_dual_channel(ctrl)) { + csbase_inc = csbase_low[log2(common_size) - 1] << 1; + } else { + csbase_inc = csbase_low[log2(common_size)]; + } + /* Compute the initial values for csbase and csbask. + * In csbase just set the enable bit and the base to zero. + * In csmask set the mask bits for the size and page level interleave. + */ + csbase = 0 | 1; + csmask = (((common_size << bits) - 1) << 21); + csmask |= 0xfe00 & ~((csbase_inc << bits) - csbase_inc); + for(index = 0; index < 8; index++) { + uint32_t value; + + value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2)); + /* Is it enabled? */ + if (!(value & 1)) { + continue; + } + pci_write_config32(ctrl->f2, DRAM_CSBASE + (index << 2), csbase); + pci_write_config32(ctrl->f2, DRAM_CSMASK + (index << 2), csmask); + csbase += csbase_inc; + } + +#if 1 + print_debug("Interleaved\r\n"); +#endif + /* Return the memory size in K */ + return common_size << (15 + bits); +} - /* Compute the memory base address address */ - base_k = 0; +static unsigned long order_chip_selects(const struct mem_controller *ctrl) +{ + unsigned long tom; + /* Remember which registers we have used in the high 8 bits of tom */ - tom = base_k >> 15; + tom = 0; for(;;) { /* Find the largest remaining canidate */ unsigned index, canidate; @@ -1270,8 +1348,19 @@ static void order_dimms(const struct mem_controller *ctrl) pci_write_config32(ctrl->f2, DRAM_CSMASK + (canidate << 2), csmask); } - tom_k = (tom & ~0xff000000) << 15; + /* Return the memory size in K */ + return (tom & ~0xff000000) << 15; +} + +static void order_dimms(const struct mem_controller *ctrl) +{ + unsigned long tom, tom_k, base_k; + unsigned node_id; + tom_k = interleave_chip_selects(ctrl); + if (!tom_k) { + tom_k = order_chip_selects(ctrl); + } /* Compute the memory base address */ base_k = 0; for(node_id = 0; node_id < ctrl->node_id; node_id++) { @@ -1287,18 +1376,13 @@ static void order_dimms(const struct mem_controller *ctrl) } tom_k += base_k; #if 0 - print_debug("tom: "); - print_debug_hex32(tom); - print_debug(" base_k: "); + print_debug("base_k: "); print_debug_hex32(base_k); print_debug(" tom_k: "); print_debug_hex32(tom_k); print_debug("\r\n"); #endif route_dram_accesses(ctrl, base_k, tom_k); -//BY LYH - fill_last(ctrl->node_id, tom_k<<2); -//BY LYH END set_top_mem(tom_k); } @@ -2063,6 +2147,10 @@ static void set_read_preamble(const struct mem_controller *ctrl, const struct me /* 166Mhz, 7.5ns */ rdpreamble = ((7 << 1)+1); } + else if (divisor == ((5 << 1)+0)) { + /* 200Mhz, 7ns */ + rdpreamble = ((7 << 1)+0); + } } else { int slots; @@ -2175,6 +2263,8 @@ static void spd_set_dram_timing(const struct mem_controller *ctrl, const struct { int dimms; int i; + int rc; + init_Tref(ctrl, param); for(i = 0; (i < 4) && ctrl->channel0[i]; i++) { int rc; @@ -2247,13 +2337,16 @@ static void sdram_enable(int controllers, const struct mem_controller *ctrl) print_debug_hex32(dcl); print_debug("\r\n"); #endif -#warning "FIXME set the ECC type to perform" -#warning "FIXME initialize the scrub registers" -#if 1 if (dcl & DCL_DimmEccEn) { + uint32_t mnc; print_debug("ECC enabled\r\n"); + mnc = pci_read_config32(ctrl[i].f3, MCA_NB_CONFIG); + mnc |= MNC_ECC_EN; + if (dcl & DCL_128BitEn) { + mnc |= MNC_CHIPKILL_EN; + } + pci_write_config32(ctrl[i].f3, MCA_NB_CONFIG, mnc); } -#endif dcl |= DCL_DisDqsHys; pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl); dcl &= ~DCL_DisDqsHys; @@ -2280,29 +2373,148 @@ static void sdram_enable(int controllers, const struct mem_controller *ctrl) } else { print_debug(" done\r\n"); } -#if 0 if (dcl & DCL_DimmEccEn) { print_debug("Clearing memory: "); - loops = 0; - dcl &= ~DCL_MemClrStatus; - pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl); + if (!is_cpu_pre_c0()) { + /* Wait until the automatic ram scrubber is finished */ + dcl &= ~(DCL_MemClrStatus | DCL_DramEnable); + pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl); + do { + dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW); + } while(((dcl & DCL_MemClrStatus) == 0) || ((dcl & DCL_DramEnable) == 0) ); + } + uint32_t base, last_scrub_k, scrub_k; + uint32_t cnt,zstart,zend; + msr_t msr,msr_201; + + /* First make certain the scrubber is disabled */ + pci_write_config32(ctrl[i].f3, SCRUB_CONTROL, + (SCRUB_NONE << 16) | (SCRUB_NONE << 8) | (SCRUB_NONE << 0)); + + /* load the start and end for the memory block to clear */ + msr_201 = rdmsr(0x201); + zstart = pci_read_config32(ctrl[0].f1, 0x40 + (i*8)); + zend = pci_read_config32(ctrl[0].f1, 0x44 + (i*8)); + zstart >>= 16; + zend >>=16; +#if 1 + print_debug("addr "); + print_debug_hex32(zstart); + print_debug("-"); + print_debug_hex32(zend); + print_debug("\r\n"); +#endif - do { - dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW); - loops += 1; - if ((loops & 1023) == 0) { - print_debug(" "); - print_debug_hex32(loops); - } - } while(((dcl & DCL_MemClrStatus) == 0) && (loops < TIMEOUT_LOOPS)); - if (loops >= TIMEOUT_LOOPS) { - print_debug("failed\r\n"); - } else { - print_debug("done\r\n"); + /* Disable fixed mtrrs */ + msr = rdmsr(MTRRdefType_MSR); + msr.lo &= ~(1<<10); + wrmsr(MTRRdefType_MSR, msr); + + /* turn on the wrap 32 disable */ + msr = rdmsr(0xc0010015); + msr.lo |= (1<<17); + wrmsr(0xc0010015,msr); + + for(;zstart>8; + wrmsr(0x200,msr); + + /* Set the limit to 64 meg of ram */ + msr.hi = 0x000000ff; + msr.lo = 0xfc000800; + wrmsr(0x201,msr); + + /* enable cache */ + __asm__ volatile( + "movl %%cr0, %0\n\t" + "andl $0x9fffffff, %0\n\t" + "movl %0, %%cr0\n\t" + :"=r" (cnt) + ); + /* Set fs base address */ + msr.lo = (zstart&0xff) << 24; + msr.hi = (zstart&0xff00) >> 8; + wrmsr(0xc0000100,msr); + + print_debug_char((zstart > 0x0ff)?'+':'-'); + + /* clear memory 64meg */ + __asm__ volatile( + "1: \n\t" + "movl %0, %%fs:(%1)\n\t" + "addl $4,%1\n\t" + "subl $1,%2\n\t" + "jnz 1b\n\t" + : + : "a" (0), "D" (0), "c" (0x01000000) + ); } - pci_write_config32(ctrl[i].f3, SCRUB_ADDR_LOW, 0); - pci_write_config32(ctrl[i].f3, SCRUB_ADDR_HIGH, 0); + + /* disable cache */ + __asm__ volatile( + "movl %%cr0, %0\n\t" + "orl $0x40000000, %0\n\t" + "movl %0, %%cr0\n\t" + :"=r" (cnt) + ); + + /* restore msr registers */ + msr = rdmsr(MTRRdefType_MSR); + msr.lo |= 0x0400; + wrmsr(MTRRdefType_MSR, msr); + + /* Restore the variable mtrrs */ + msr.lo = 6; + msr.hi = 0; + wrmsr(0x200,msr); + wrmsr(0x201,msr_201); + + /* Set fs base to 0 */ + msr.lo = 0; + msr.hi = 0; + wrmsr(0xc0000100,msr); + + /* enable cache */ + __asm__ volatile( + "movl %%cr0, %0\n\t" + "andl $0x9fffffff, %0\n\t" + "movl %0, %%cr0\n\t" + :"=r" (cnt) + ); + + /* turn off the wrap 32 disable */ + msr = rdmsr(0xc0010015); + msr.lo &= ~(1<<17); + wrmsr(0xc0010015,msr); + + /* Find the Srub base address for this cpu */ + base = pci_read_config32(ctrl[i].f1, 0x40 + (ctrl[i].node_id << 3)); + base &= 0xffff0000; + + /* Set the scrub base address registers */ + pci_write_config32(ctrl[i].f3, SCRUB_ADDR_LOW, base << 8); + pci_write_config32(ctrl[i].f3, SCRUB_ADDR_HIGH, base >> 24); + + /* Enable scrubbing at the lowest possible rate */ + pci_write_config32(ctrl[i].f3, SCRUB_CONTROL, + (SCRUB_84ms << 16) | (SCRUB_84ms << 8) | (SCRUB_84ms << 0)); + + print_debug("done\r\n"); } -#endif } } -- cgit v1.2.3