From 16f8c584ccb51dcc6ea5ff2ed5cf785f5cc0aa7b Mon Sep 17 00:00:00 2001 From: David Schleef Date: Mon, 18 Dec 2000 16:36:34 +0000 Subject: [PATCH] changes from Chris Baugher --- comedi/drivers/das16-new.c | 502 ++++++++++++++++++++++++++++++++----- 1 file changed, 445 insertions(+), 57 deletions(-) diff --git a/comedi/drivers/das16-new.c b/comedi/drivers/das16-new.c index 49dbcea7..93a4853f 100644 --- a/comedi/drivers/das16-new.c +++ b/comedi/drivers/das16-new.c @@ -4,6 +4,7 @@ COMEDI - Linux Control and Measurement Device Interface Copyright (C) 2000 David A. Schleef + Copyright (C) 2000 Chris R. Baugher This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -114,32 +115,46 @@ #define DAS16_STATUS 8 #define DAS16_EOC (1<<7) #define DAS16_UB (1<<6) -#define DAS16_MUXbit (1<<5) -#define DAS16_INT (1<<4) +#define DAS16_MUXBIT (1<<5) #define DAS16_INT (1<<4) #define DAS16_CONTROL 9 #define DAS16_INTE (1<<7) #define DAS16_IRQ(x) ((x)<<4) #define DAS16_DMA (1<<2) -#define DAS16_TS(x) ((x)<<0) -#define DAS16_PACER 0x0a +#define DAS16_CTR2 0x03 +#define DAS16_IP0 0x02 +#define DAS16_SOFT 0x00 +#define DAS16_PACER 0x0A #define DAS16_CTR0 (1<<1) #define DAS16_TRIG0 (1<<0) -#define DAS16_GAIN 0x0b +#define DAS16_GAIN 0x0B +#define DAS16_CNTR0_DATA 0x0C +#define DAS16_CNTR1_DATA 0x0D +#define DAS16_CNTR2_DATA 0x0E +#define DAS16_CNTR_CONTROL 0x0F +#define DAS16_TERM_CNT 0x00 +#define DAS16_ONE_SHOT 0x02 +#define DAS16_RATE_GEN 0x04 +#define DAS16_CNTR_LSB_MSB 0x30 +#define DAS16_CNTR0 0x00 +#define DAS16_CNTR1 0x40 +#define DAS16_CNTR2 0x80 #define DAS1600_CONV 0x404 +#define DAS1600_CONV_DISABLE 0x40 #define DAS1600_BURST 0x405 #define DAS1600_BURST_VAL 0x40 #define DAS1600_ENABLE 0x406 #define DAS1600_ENABLE_VAL 0x40 -#define DAS1600_BURST_STATUS 0x407 -#define DAS1600_BME (1<<7) -#define DAS1600_ME (1<<6) -#define DAS1600_CD (1<<5) -#define DAS1600_WS (1<<1) -#define DAS1600_CLK (1<<0) - +#define DAS1600_STATUS_B 0x407 +#define DAS1600_BME 0x40 +#define DAS1600_ME 0x20 +#define DAS1600_CD 0x10 +#define DAS1600_WS 0x02 +#define DAS1600_CLK_10MHZ 0x01 +#define DAS16_SLOWEST_TIMER 429000000000 +#define DAS16_FASTEST_TIMER 10000 static comedi_lrange range_das1x01_bip = { 4, { BIP_RANGE( 10 ), @@ -211,6 +226,17 @@ static int das16_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *in static int das16_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); static int das16_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int das16_cmd_test(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd); +static int das16_cmd_exec(comedi_device *dev,comedi_subdevice *s); +static int das16_cancel(comedi_device *dev, comedi_subdevice *s); + +static void das16_reset(comedi_device *dev); +static void das16_interrupt(int irq, void *d, struct pt_regs *regs); + +static float das16_set_pacer(comedi_device *dev, unsigned int ns); +static int das1600_mode_detect(comedi_device *dev); +static void reg_dump(comedi_device *dev); + struct das16_board_struct{ char *name; void *ai; @@ -400,17 +426,165 @@ comedi_driver driver_das16={ }; -#define TIMEOUT 1000 +#define DAS16_TIMEOUT 1000 struct das16_private_struct { unsigned int ai_unipolar; unsigned int ai_singleended; unsigned int clockbase; + unsigned char control_state; + volatile unsigned char cmd_go; + unsigned int adc_count; }; #define devpriv ((struct das16_private_struct *)(dev->private)) #define thisboard ((struct das16_board_struct *)(dev->board_ptr)) +static int das16_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd) +{ + int err=0, tmp; + + /* make sure triggers are valid */ + tmp=cmd->start_src; + cmd->start_src &= TRIG_NOW; + if(!cmd->start_src && tmp!=cmd->start_src)err++; + + tmp=cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_FOLLOW|TRIG_TIMER; + if(!cmd->scan_begin_src && tmp!=cmd->scan_begin_src)err++; + + /* XXX This must be TRIG_FOLLOW until I figure out a way to * + * time the individual conversions. */ + tmp=cmd->convert_src; + //cmd->convert_src &= TRIG_TIMER; + cmd->convert_src &= TRIG_FOLLOW; + if(!cmd->convert_src && tmp!=cmd->convert_src)err++; + + tmp=cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if(!cmd->scan_end_src && tmp!=cmd->scan_end_src)err++; + + tmp=cmd->stop_src; + cmd->stop_src &= TRIG_COUNT|TRIG_NONE; + if(!cmd->stop_src && tmp!=cmd->stop_src)err++; + + if(err)return 1; + + /* step 2: make sure trigger sources are unique and mutually compatible */ + /* note that mutual compatiblity is not an issue here */ + if(cmd->scan_begin_src!=TRIG_FOLLOW && + cmd->scan_begin_src!=TRIG_EXT && + cmd->scan_begin_src!=TRIG_TIMER)err++; + if(cmd->stop_src!=TRIG_COUNT && + cmd->stop_src!=TRIG_NONE)err++; + + if(err)return 2; + + /* step 3: make sure arguments are trivially compatible */ + if(cmd->start_arg!=0){ + cmd->start_arg=0; + err++; + } + +#if 0 + if(cmd->scan_begin_src==TRIG_FOLLOW){ + /* internal trigger */ + if(cmd->scan_begin_arg!=0){ + cmd->scan_begin_arg=0; + err++; + } + }else{ + /* external trigger */ + /* should be level/edge, hi/lo specification here */ + if(cmd->scan_begin_arg!=0){ + cmd->scan_begin_arg=0; + err++; + } + } +#endif + + if(cmd->scan_begin_argscan_begin_arg=DAS16_FASTEST_TIMER; + err++; + } + + if(cmd->scan_begin_arg>DAS16_SLOWEST_TIMER){ + cmd->scan_begin_arg=DAS16_SLOWEST_TIMER; + err++; + } + + if(cmd->scan_end_arg!=cmd->chanlist_len){ + cmd->scan_end_arg=cmd->chanlist_len; + err++; + } + + if(cmd->stop_src==TRIG_COUNT){ + /* any count is allowed */ + }else{ + /* TRIG_NONE */ + if(cmd->stop_arg!=0){ + cmd->stop_arg=0; + err++; + } + } + + if(err)return 3; + + return 0; +} + +static int das16_cmd_exec(comedi_device *dev,comedi_subdevice *s) +{ + comedi_cmd *cmd = &s->cmd; + char byte; + float freq; + + devpriv->adc_count = cmd->stop_arg*cmd->chanlist_len; + devpriv->cmd_go = 1; + + /* check if we are scanning multiple channels */ + if(cmd->chanlist_len < 2) { /* one channel */ + byte = CR_CHAN(cmd->chanlist[0]); + outb(byte, dev->iobase+DAS16_MUX); + } else { /* multiple channels */ + byte = CR_CHAN(cmd->chanlist[0]) + + (CR_CHAN(cmd->chanlist[cmd->chanlist_len-1]<<4)); + outb(byte, dev->iobase+DAS16_MUX); + } + + /* enable pacer clocked conversions */ + devpriv->control_state |= DAS16_CTR2; + outb(devpriv->control_state,dev->iobase+DAS16_CONTROL); + + /* set counter mode and counts */ + freq = das16_set_pacer(dev, cmd->scan_begin_arg); + + printk("pacer frequency: %d\n", (int)freq); + /* enable counters */ + outb(0x00,dev->iobase+DAS16_PACER); + /* clear interrupt bit */ + outb(0x00,dev->iobase+DAS16_STATUS); + /* enable interrupts */ + devpriv->control_state |= DAS16_INTE; + outb(devpriv->control_state,dev->iobase+DAS16_CONTROL); + + return 0; +} + +static int das16_cancel(comedi_device *dev, comedi_subdevice *s) +{ + devpriv->cmd_go = 0; + + return 0; +} + +static void das16_reset(comedi_device *dev) +{ + outb(0,dev->iobase+DAS16_STATUS); + outb(0,dev->iobase+DAS16_CONTROL); + outb(0,dev->iobase+DAS16_PACER); + outb(0,dev->iobase+DAS16_CNTR_CONTROL); +} static int das16_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) { @@ -436,16 +610,16 @@ static int das16_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *in /* How long should we wait for MUX to settle? */ //udelay(5); - + for(n=0;nn;n++){ /* trigger conversion */ outb_p(0,dev->iobase+DAS16_TRIG); - for(i=0;iao_nbits==12){ - lsb=(data[0]<<4)&0xff; - msb=(data[0]>>4)&0xff; - }else{ - lsb=data[0]&0xff; - msb=(data[0]>>8)&0xff; - } - chan=CR_CHAN(insn->chanspec); - for(n=0;nn;n++){ + for(i=0;in;i++){ + if(thisboard->ao_nbits==12){ + lsb=(data[i]<<4)&0xff; + msb=(data[i]>>4)&0xff; + }else{ + lsb=data[i]&0xff; + msb=(data[i]>>8)&0xff; + } + #if 0 outb(lsb,dev->iobase+devpriv->ao_offset_lsb[chan]); outb(msb,dev->iobase+devpriv->ao_offset_msb[chan]); @@ -499,15 +673,167 @@ static int das16_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *in outb(lsb,dev->iobase+DAS16_AO_LSB(chan)); outb(msb,dev->iobase+DAS16_AO_MSB(chan)); #endif - /* XXX */ - break; } return n; } +static void das16_interrupt(int irq, void *d, struct pt_regs *regs) +{ + static int i, j; + static char lsb, msb; + + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices; + + if(devpriv->cmd_go) { /* Are we supposed to be here? */ + /* check for missed conversions */ + // if( CR_CHAN(s->cur_chanlist[0]) != inb() ) + // printk("Channel MUX sync error\n"); + + for(i=0;icur_chanlist_len;++i) { + + if(i) { /* performing multiple conversions */ + outb(0x00, dev->iobase+DAS16_TRIG); /* force a conversion */ + + for(j=0;jiobase+DAS16_STATUS)&DAS16_EOC) ) + break; + if(j==DAS16_TIMEOUT) + printk("das16 EOC timeout!!"); + } + + lsb = inb(dev->iobase+DAS16_AI_LSB); + msb = inb(dev->iobase+DAS16_AI_MSB); + + /* all this just to put data into a buffer! */ + *(sampl_t *)(((void *)s->cur_trig.data)+s->buf_int_ptr) = + (lsb>>4) + (msb<<4); + + s->buf_int_ptr += sizeof(sampl_t); + s->buf_int_count += sizeof(sampl_t); + + if(s->buf_int_ptr >= s->cur_trig.data_len) { /* buffer rollover */ + s->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + + if( !(s->cmd.flags&TRIG_WAKE_EOS) ) + comedi_bufcheck(dev, s); /* wakeup user's read() */ + + if(--devpriv->adc_count <= 0) { /* end of acquisition */ + printk("End of acquisition\n"); + devpriv->cmd_go = 0; + devpriv->control_state &= ~DAS16_INTE; + outb(devpriv->control_state, dev->iobase+DAS16_CONTROL); + comedi_done(dev, s); + break; + } + + } /* end of scan loop */ + + comedi_eos(dev, s); + + } else { + printk("Acquisition canceled OR Stray interrupt!\n"); + devpriv->control_state &= ~DAS16_INTE; + outb(devpriv->control_state, dev->iobase+DAS16_CONTROL); + comedi_error_done(dev, s); + } + + /* clear interrupt */ + outb(0x00,dev->iobase+DAS16_STATUS); +} + +/* This function takes a time in nanoseconds and sets the * + * 2 pacer clocks to the closest frequency possible. It also * + * returns the actual sampling rate. */ +static float das16_set_pacer(comedi_device *dev, unsigned int ns) +{ + short unsigned ctr1, ctr2; + unsigned int mask; + long product, error; + + /* divide 10Mhz by frequency */ + product = ( devpriv->clockbase/(1000000000.0/ns) ) + 0.5; + /* Now the job is to find two 16 bit numbers, that when multiplied + together are approximately equal to product. Start by setting + one of them, ctr1 to 2 (minimum settable value) and increment until + the error is minimized and ctr2 is less than 32768. + + NOTE: In Mode 2, a value of 1 is illegal! Therefore, crt1 and crt2 + can never be 1. + + */ + + printk("product: %ld\n", product); + ctr1 = product / 32768; + if (ctr1 < 2) + ctr1 = 2; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + + while (error && ctr1 < 32768 && ctr2 > 1) { + ctr1++; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + } + + /* the frequency is prime, add 1 to it */ + if (error) { + product++; + ctr1 = product / 32768; + if (ctr1 < 2) + ctr1 = 2; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + + while (error && ctr1 < 32768 && ctr2 > 1) { + ctr1++; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + } + } + /* we can't have ctr2 equal to 1, or system hangs */ + if (ctr2 == 1) { + ctr2++; + ctr1 /= 2; + } + printk("ctr1: %d, ctr2: %d\n", ctr1, ctr2); + /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ + mask = DAS16_CNTR2 | DAS16_RATE_GEN | DAS16_CNTR_LSB_MSB; + outb(mask, dev->iobase+DAS16_CNTR_CONTROL); + + outb(ctr2 & 0xFF , dev->iobase + DAS16_CNTR2_DATA); + outb(ctr2 >> 8, dev->iobase + DAS16_CNTR2_DATA); + + mask = DAS16_CNTR1 | DAS16_RATE_GEN | DAS16_CNTR_LSB_MSB; + outb(mask, dev->iobase+DAS16_CNTR_CONTROL); + + outb(ctr1 & 0xFF, dev->iobase + DAS16_CNTR1_DATA); + outb(ctr1 >> 8, dev->iobase + DAS16_CNTR1_DATA); + +// printk("SetPacerFreq: Pacer Register set to %#x\n", BoardData.pacerReg); + + return devpriv->clockbase / ((long) ctr1 * (long) ctr2) + 0.5; +} +static void reg_dump(comedi_device *dev) +{ + printk("********DAS1600 REGISTER DUMP********\n"); + printk("DAS16_MUX: %x\n", inb(dev->iobase+DAS16_MUX) ); + printk("DAS16_DIO: %x\n", inb(dev->iobase+DAS16_DIO) ); + printk("DAS16_STATUS: %x\n", inb(dev->iobase+DAS16_STATUS) ); + printk("DAS16_CONTROL: %x\n", inb(dev->iobase+DAS16_CONTROL) ); + printk("DAS16_PACER: %x\n", inb(dev->iobase+DAS16_PACER) ); + printk("DAS16_GAIN: %x\n", inb(dev->iobase+DAS16_GAIN) ); + printk("DAS16_CNTR_CONTROL: %x\n", inb(dev->iobase+DAS16_CNTR_CONTROL) ); + printk("DAS1600_CONV: %x\n", inb(dev->iobase+DAS1600_CONV) ); + printk("DAS1600_BURST: %x\n", inb(dev->iobase+DAS1600_BURST) ); + printk("DAS1600_ENABLE: %x\n", inb(dev->iobase+DAS1600_ENABLE) ); + printk("DAS1600_STATUS_B: %x\n", inb(dev->iobase+DAS1600_STATUS_B) ); +} static int detect_ao(comedi_device *dev) { @@ -536,10 +862,9 @@ static int detect_ao(comedi_device *dev) return 0; } -static int das16_probe(comedi_device *dev) +static int das16_probe(comedi_device *dev, comedi_devconfig *it) { int status; - int burststatus; int diobits; /* status is available on all boards */ @@ -562,14 +887,19 @@ static int das16_probe(comedi_device *dev) diobits = inb(dev->iobase + DAS16_DIO) & 0xf0; -printk("diobits 0x%02x",diobits); + printk(" diobits 0x%02x",diobits); + switch(diobits){ case 0x80: printk(" das16 or das16/f"); /* only difference is speed, so not an issue yet */ + //devpriv->clockbase = it->options[4]; + devpriv->clockbase = 10000000; return das16_board_das16; case 0x00: printk(" das16jr or das16/330"); + //devpriv->clockbase = it->options[4]; + devpriv->clockbase = 10000000; /* the 330 has ao, 16jr does not */ /* we can write the low 4 bits without updating DAC */ @@ -578,20 +908,38 @@ printk("diobits 0x%02x",diobits); }else{ return das16_board_das16jr; } - - break; + case 0xC0: + printk(" das1600\n"); + das1600_mode_detect(dev); + return das16_board_das1601_12; + case 0xE0: + printk(" das1400\n"); + das1600_mode_detect(dev); + return das16_board_das1401_12; default: - printk(" unknown board"); + printk(" unknown board\n"); return -1; - case 0xc0: - printk(" das1600 or das1400"); - break; } +} - /* burststatus is available on 1600, 1400 */ - - burststatus = inb(dev->iobase + DAS1600_BURST_STATUS); +static int das1600_mode_detect(comedi_device *dev) +{ + int status=0; + + status = inb(dev->iobase + DAS1600_STATUS_B); + + if(status & DAS1600_CLK_10MHZ) { + devpriv->clockbase = 10000000; + printk(" 10MHz pacer clock\n"); + } else { + devpriv->clockbase = 1000000; + printk(" 1MHz pacer clock\n"); + } + /* enable das1400/1600 mode */ +// outb(DAS1600_ENABLE_VAL, dev->iobase+DAS1600_ENABLE); +#ifdef DETECT + /* burststatus is available on 1600, 1400 */ if((burststatus & 0xfc)==0x10){ /* true for 1400, 1600 */ } @@ -602,8 +950,6 @@ printk("diobits 0x%02x",diobits); devpriv->clockbase = 1000; } - outb(DAS1600_ENABLE_VAL,dev->iobase+DAS1600_ENABLE); - if(detect_ao(dev)){ printk("das1600 series\n"); return das16_board_das1601_12; @@ -611,7 +957,9 @@ printk("diobits 0x%02x",diobits); printk("das1400 series\n"); return das16_board_das1401_12; } +#endif + return 0; } @@ -633,12 +981,14 @@ static int das16_recognize(char *name) * Options list: * 0 I/O base * 1 IRQ + * 2 DMA + * 3 Clock speed */ static int das16_attach(comedi_device *dev, comedi_devconfig *it) { comedi_subdevice *s; - int ret; + int ret, irq; dev->iobase = it->options[0]; @@ -647,25 +997,32 @@ static int das16_attach(comedi_device *dev, comedi_devconfig *it) if((ret=alloc_private(dev,sizeof(struct das16_private_struct)))<0) return ret; - dev->board = das16_probe(dev); + dev->board = das16_probe(dev, it); dev->board_ptr = das16_boards + dev->board; dev->board_name = thisboard->name; + if(thisboard->size<0x400){ - printk(" 0x%04x-0x%04x", - dev->iobase,dev->iobase+thisboard->size); + printk(" 0x%04x-0x%04x\n", + dev->iobase,dev->iobase+thisboard->size); if(check_region(dev->iobase,thisboard->size)<0){ printk(" I/O port conflict\n"); return -EIO; } }else{ - printk(" 0x%04x-0x%04x 0x%04x-0x%04x", - dev->iobase,dev->iobase+0x0f, - dev->iobase+0x400,dev->iobase+(thisboard->size&0x3ff)); - if(check_region(dev->iobase,0x10)<0 || - check_region(dev->iobase+0x400,thisboard->size&0x3ff)<0){ - printk(" I/O port conflict\n"); + printk(" 0x%04x-0x%04x 0x%04x-0x%04x\n", + dev->iobase,dev->iobase+0x0f, + dev->iobase+0x400,dev->iobase+0x400+(thisboard->size&0x3ff)); + if(check_region(dev->iobase,0x10) < 0) { + printk(" I/O port conflict: 0x%04x-0x%04x\n", + dev->iobase,dev->iobase+0x0f); + return -EIO; + } + if(check_region(dev->iobase+0x400,thisboard->size&0x3ff)<0){ + printk(" I/O port conflict: 0x%04x-0x%04x\n", + dev->iobase+0x400, + dev->iobase+0x400+(thisboard->size&0x3ff)); return -EIO; } } @@ -678,9 +1035,20 @@ static int das16_attach(comedi_device *dev, comedi_devconfig *it) request_region(dev->iobase,thisboard->size,"das16"); }else{ request_region(dev->iobase,0x10,"das16"); - request_region(dev->iobase,thisboard->size&0x3ff,"das16"); + request_region(dev->iobase+0x400,thisboard->size&0x3ff,"das16"); } + /* now for the irq */ + irq=it->options[1]; + if(irq>0){ + if((ret=request_irq(irq,das16_interrupt,SA_INTERRUPT,"das16",dev))<0) + return ret; + dev->irq=irq; + printk(" ( irq = %d )\n",irq); + } else if(irq == 0){ + printk(" ( no irq )\n"); + } + s=dev->subdevices+0; /* ai */ if(thisboard->ai){ @@ -693,6 +1061,7 @@ static int das16_attach(comedi_device *dev, comedi_devconfig *it) s->n_chan = 8; s->subdev_flags |= SDF_DIFF; } + s->len_chanlist = 16; s->maxdata = (1<ai_nbits)-1; if(devpriv->ai_unipolar){ s->range_table = das16_ai_uni_lranges[thisboard->ai_pg]; @@ -700,6 +1069,10 @@ static int das16_attach(comedi_device *dev, comedi_devconfig *it) s->range_table = das16_ai_bip_lranges[thisboard->ai_pg]; } s->insn_read = thisboard->ai; + s->do_cmdtest = das16_cmd_test; + s->do_cmd = das16_cmd_exec; + s->cancel = das16_cancel; + s->cmd.flags |= TRIG_WAKE_EOS; }else{ s->type=COMEDI_SUBD_UNUSED; } @@ -752,8 +1125,10 @@ static int das16_attach(comedi_device *dev, comedi_devconfig *it) s->type = COMEDI_SUBD_UNUSED; } - outb(DAS16_IRQ(dev->irq),dev->iobase+DAS16_CONTROL); - outb(0,DAS16_PACER); + das16_reset(dev); + /* set the interrupt level,enable pacer clock */ + devpriv->control_state = DAS16_IRQ(dev->irq); + outb(devpriv->control_state,dev->iobase+DAS16_CONTROL); return 0; } @@ -761,7 +1136,20 @@ static int das16_attach(comedi_device *dev, comedi_devconfig *it) static int das16_detach(comedi_device *dev) { - + printk("comedi%d: das16: remove\n", dev->minor); + + das16_reset(dev); + + if(dev->irq) + free_irq(dev->irq, dev); + + if(thisboard->size<0x400){ + release_region(dev->iobase,thisboard->size); + }else{ + release_region(dev->iobase,0x10); + release_region(dev->iobase+0x400,thisboard->size&0x3ff); + } + return 0; } -- 2.26.2