From 7f82b9b63e8facb9a581fff98ae740d3be2547aa Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Sun, 29 Apr 2007 21:41:33 +0000 Subject: [PATCH] Allocate async buffer pages with dma_alloc_coherent, and map them into continguous virtual addresses with vmap. --- comedi/comedi_fops.c | 4 +- comedi/drivers.c | 108 +++++++++++++++++++++++++-------- comedi/drivers/mite.c | 5 +- comedi/drivers/mite.h | 9 ++- comedi/drivers/ni_mio_common.c | 38 +++++++----- comedi/drivers/ni_pcidio.c | 2 + comedi/drivers/ni_pcimio.c | 1 + comedi/drivers/ni_stc.h | 8 +++ include/linux/comedidev.h | 30 ++++++++- 9 files changed, 157 insertions(+), 48 deletions(-) diff --git a/comedi/comedi_fops.c b/comedi/comedi_fops.c index f9aceb0b..cdea1bf0 100644 --- a/comedi/comedi_fops.c +++ b/comedi/comedi_fops.c @@ -1309,8 +1309,8 @@ static int comedi_mmap(struct file * file, struct vm_area_struct *vma) return -EFAULT; n_pages = size >> PAGE_SHIFT; - for(i=0;ibuf_page_list[i]) >> PAGE_SHIFT, + for(i = 0; i < n_pages; ++i){ + if(remap_pfn_range(vma, start, page_to_pfn(virt_to_page(async->prealloc_buf + PAGE_SIZE * i)), PAGE_SIZE, PAGE_SHARED)){ return -EAGAIN; } diff --git a/comedi/drivers.c b/comedi/drivers.c index 08d17089..c678a9cd 100644 --- a/comedi/drivers.c +++ b/comedi/drivers.c @@ -40,6 +40,7 @@ #include /* for SuSE brokenness */ #include #include +#include #include #include @@ -92,6 +93,7 @@ static void cleanup_device(comedi_device *dev) kfree(dev->private); dev->private = NULL; } + module_put(dev->driver->module); dev->driver = 0; dev->board_name = NULL; dev->board_ptr = NULL; @@ -101,6 +103,7 @@ static void cleanup_device(comedi_device *dev) dev->write_subdev = NULL; dev->open = NULL; dev->close = NULL; + comedi_set_hw_dev(dev, NULL); } static int __comedi_device_detach(comedi_device *dev) @@ -111,7 +114,6 @@ static int __comedi_device_detach(comedi_device *dev) }else{ printk("BUG: dev->driver=NULL in comedi_device_detach()\n"); } - module_put(dev->driver->module); cleanup_device(dev); return 0; } @@ -430,48 +432,106 @@ int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s, unsigned long new_size) { comedi_async *async = s->async; - + unsigned long adr; + /* if no change is required, do nothing */ if(async->prealloc_buf && async->prealloc_bufsz == new_size){ return 0; } - - if(async->prealloc_bufsz){ + // cleanup old buffer + if(async->prealloc_bufsz) + { int i; - int n_pages = async->prealloc_bufsz >> PAGE_SHIFT; + unsigned n_pages = async->prealloc_bufsz >> PAGE_SHIFT; - for(i=0;ibuf_page_list[i])))); + adr = (unsigned long) async->prealloc_buf; + for(i = 0; i < n_pages; i++){ + mem_map_unreserve(virt_to_page(adr)); + adr += PAGE_SIZE; + } + async->prealloc_bufsz = 0; + } + if(async->prealloc_buf) + { + if(s->async_dma_dir != DMA_NONE) + { + vunmap(async->prealloc_buf); + }else + { + vfree(async->prealloc_buf); } - - vfree(async->prealloc_buf); async->prealloc_buf = NULL; - kfree(async->buf_page_list); + } + if(async->buf_page_list) + { + unsigned i; + for(i = 0; i < async->n_buf_pages; ++i) + { + if(async->buf_page_list[i].virt_addr) + { + dma_free_coherent(dev->hw_dev, PAGE_SIZE, + async->buf_page_list[i].virt_addr, async->buf_page_list[i].dma_addr); + } + } + vfree(async->buf_page_list); async->buf_page_list = NULL; + async->n_buf_pages = 0; } - + // allocate new buffer if(new_size){ - unsigned long adr; - int n_pages = new_size >> PAGE_SHIFT; int i; + unsigned n_pages = (new_size + PAGE_SIZE - 1) >> PAGE_SHIFT; - async->buf_page_list = kmalloc(sizeof(unsigned long)*n_pages, GFP_KERNEL); - async->prealloc_buf = vmalloc_32(new_size); - if(async->prealloc_buf == NULL){ - async->prealloc_bufsz = 0; - kfree(async->buf_page_list); - return -ENOMEM; + // size is rounded up to nearest multiple of PAGE_SIZE + new_size = n_pages << PAGE_SHIFT; + + if(s->async_dma_dir != DMA_NONE) + { + struct page** pages = NULL; + + async->buf_page_list = vmalloc(sizeof(struct comedi_buf_page) * n_pages); + if(async->buf_page_list == NULL) + { + return -ENOMEM; + } + memset(async->buf_page_list, 0, sizeof(struct comedi_buf_page) * n_pages); + async->n_buf_pages = n_pages; + + pages = vmalloc(sizeof(struct page*) * n_pages); + if(pages == NULL) + { + return -ENOMEM; + } + for(i = 0; i < n_pages; i++) + { + async->buf_page_list[i].virt_addr = dma_alloc_coherent(dev->hw_dev, + PAGE_SIZE, &async->buf_page_list[i].dma_addr, GFP_KERNEL | __GFP_COMP); + if(async->buf_page_list[i].virt_addr == NULL) + { + vfree(pages); + return -ENOMEM; + } + pages[i] = virt_to_page(async->buf_page_list[i].virt_addr); + } + async->prealloc_buf = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE); + vfree(pages); + if(async->prealloc_buf == NULL) return -ENOMEM; + }else + { + async->prealloc_buf = vmalloc(new_size); + if(async->prealloc_buf == NULL) + { + return -ENOMEM; + } } - memset(async->prealloc_buf,0,new_size); adr = (unsigned long)async->prealloc_buf; - for(i=0;ibuf_page_list[i] = kvirt_to_kva(adr); - mem_map_reserve(virt_to_page(__va(__pa(async->buf_page_list[i])))); + for(i = 0; i < n_pages; i++) + { + mem_map_reserve(virt_to_page(adr)); adr += PAGE_SIZE; } } - async->prealloc_bufsz = new_size; return 0; diff --git a/comedi/drivers/mite.c b/comedi/drivers/mite.c index 7849f8f8..29a980b0 100644 --- a/comedi/drivers/mite.c +++ b/comedi/drivers/mite.c @@ -196,7 +196,6 @@ void mite_unsetup(struct mite_struct *mite) if(!mite)return; - pci_disable_device(mite->pcidev); if(mite->mite_io_addr){ iounmap(mite->mite_io_addr); mite->mite_io_addr=NULL; @@ -308,8 +307,8 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring, comedi_async *async) for(i = 0; i < n_links; i++){ ring->descriptors[i].count = cpu_to_le32(PAGE_SIZE); - ring->descriptors[i].addr = cpu_to_le32(virt_to_bus( - (void *)async->buf_page_list[i])); + ring->descriptors[i].addr = cpu_to_le32(async->buf_page_list[i].dma_addr); + // FIXME: virt_to_bus is deprecated ring->descriptors[i].next = cpu_to_le32(virt_to_bus( ring->descriptors + i + 1)); } diff --git a/comedi/drivers/mite.h b/comedi/drivers/mite.h index 15933f2a..6a2a4248 100644 --- a/comedi/drivers/mite.h +++ b/comedi/drivers/mite.h @@ -350,11 +350,14 @@ enum ConfigRegister_bits }; static inline int CR_REQS(int source) { - return (source & 0x7) <<16; + return (source & 0x7) << 16; }; -static inline int CR_REQSDRQ(int drq_line) +static inline int CR_REQSDRQ(unsigned drq_line) { - return CR_REQS(drq_line + 0x4); + /* m-series are supposed to have 6 dma channels, but + I not sure how to set the drq line on the mite when + using channels 4 or 5. */ + return CR_REQS((drq_line & 0x3) | 0x4); } static inline int CR_RL(unsigned int retry_limit) { diff --git a/comedi/drivers/ni_mio_common.c b/comedi/drivers/ni_mio_common.c index 54e26920..54c38465 100644 --- a/comedi/drivers/ni_mio_common.c +++ b/comedi/drivers/ni_mio_common.c @@ -311,6 +311,7 @@ static void get_last_sample_6143( comedi_device *dev ); static int ni_ai_drain_dma(comedi_device *dev ); /* DMA channel setup */ + static inline void ni_set_ai_dma_channel(comedi_device *dev, int channel) { unsigned long flags; @@ -319,7 +320,7 @@ static inline void ni_set_ai_dma_channel(comedi_device *dev, int channel) devpriv->ai_ao_select_reg &= ~AI_DMA_Select_Mask; if(channel >= 0) { - devpriv->ai_ao_select_reg |= (1 << (channel + AI_DMA_Select_Shift)) & AI_DMA_Select_Mask; + devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AI_DMA_Select_Shift) & AI_DMA_Select_Mask; } ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select); mmiowb(); @@ -334,7 +335,7 @@ static inline void ni_set_ao_dma_channel(comedi_device *dev, int channel) devpriv->ai_ao_select_reg &= ~AO_DMA_Select_Mask; if(channel >= 0) { - devpriv->ai_ao_select_reg |= (1 << (channel + AO_DMA_Select_Shift)) & AO_DMA_Select_Mask; + devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AO_DMA_Select_Shift) & AO_DMA_Select_Mask; } ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select); mmiowb(); @@ -344,6 +345,8 @@ static inline void ni_set_ao_dma_channel(comedi_device *dev, int channel) static int ni_request_ai_mite_channel(comedi_device *dev) { unsigned long flags; + /* really should be 5 for m-series, but I don't know how to get + mite dma channels 4 and 5 working */ static const unsigned max_dma_channel = 3; comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); @@ -363,6 +366,8 @@ static int ni_request_ai_mite_channel(comedi_device *dev) static int ni_request_ao_mite_channel(comedi_device *dev) { unsigned long flags; + /* really should be 5 for m-series, but I don't know how to get + mite dma channels 4 and 5 working */ static const unsigned max_dma_channel = 3; comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); @@ -1239,7 +1244,7 @@ static int ni_ai_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd) retval = ni_request_ai_mite_channel(dev); if(retval) return retval; - //rt_printk("comedi_debug: using mite channel %i for ai.\n", devpriv->ai_mite_chan->channel); +// rt_printk("comedi_debug: using mite channel %i for ai.\n", devpriv->ai_mite_chan->channel); /* write alloc the entire buffer */ comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz); @@ -2513,7 +2518,7 @@ static int ni_ao_insn_write(comedi_device *dev,comedi_subdevice *s, { unsigned int chan = CR_CHAN(insn->chanspec); unsigned int invert; - + invert = ni_ao_config_chanlist(dev,s,&insn->chanspec, 1, 0); devpriv->ao[chan] = data[0]; @@ -3392,29 +3397,33 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it) s->cancel = &ni_ai_reset; s->poll = &ni_ai_poll; s->munge = &ni_ai_munge; +#ifdef PCIDMA + s->async_dma_dir = DMA_FROM_DEVICE; +#endif }else{ s->type=COMEDI_SUBD_UNUSED; } /* analog output subdevice */ - s=dev->subdevices+1; + s = dev->subdevices + 1; if(boardtype.n_aochan){ - s->type=COMEDI_SUBD_AO; - s->subdev_flags=SDF_WRITABLE|SDF_DEGLITCH|SDF_GROUND; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND; if(boardtype.reg_type & ni_reg_m_series_mask) s->subdev_flags |= SDF_SOFT_CALIBRATED; - s->n_chan=boardtype.n_aochan; - s->maxdata=(1<n_chan = boardtype.n_aochan; + s->maxdata = (1 << boardtype.aobits) - 1; s->range_table = boardtype.ao_range_table; - s->insn_read=ni_ao_insn_read; + s->insn_read = &ni_ao_insn_read; if(boardtype.reg_type & ni_reg_6xxx_mask){ - s->insn_write=ni_ao_insn_write_671x; + s->insn_write = &ni_ao_insn_write_671x; }else{ - s->insn_write=ni_ao_insn_write; + s->insn_write = &ni_ao_insn_write; } #ifdef PCIDMA if(boardtype.n_aochan){ + s->async_dma_dir = DMA_TO_DEVICE; #else if(boardtype.ao_fifo_depth){ #endif @@ -3427,9 +3436,9 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it) if((boardtype.reg_type & ni_reg_m_series_mask) == 0) s->munge=ni_ao_munge; } - s->cancel=ni_ao_reset; + s->cancel = &ni_ao_reset; }else{ - s->type=COMEDI_SUBD_UNUSED; + s->type = COMEDI_SUBD_UNUSED; } if((boardtype.reg_type & ni_reg_67xx_mask)) init_ao_67xx(dev, s); @@ -3586,6 +3595,7 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it) s->do_cmd = ni_gpct_cmd; s->do_cmdtest = ni_gpct_cmdtest; s->cancel = ni_gpct_cancel; + s->async_dma_dir = DMA_BIDIRECTIONAL; s->private = &devpriv->counters[j]; devpriv->counters[j].dev = dev; diff --git a/comedi/drivers/ni_pcidio.c b/comedi/drivers/ni_pcidio.c index a0004088..9f9f1eae 100644 --- a/comedi/drivers/ni_pcidio.c +++ b/comedi/drivers/ni_pcidio.c @@ -1141,6 +1141,7 @@ static int nidio_attach(comedi_device *dev,comedi_devconfig *it) printk("error setting up mite\n"); return ret; } + comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev); dev->board_name=this_board->name; irq=mite_irq(devpriv->mite); @@ -1184,6 +1185,7 @@ static int nidio_attach(comedi_device *dev,comedi_devconfig *it) s->cancel = ni_pcidio_cancel; s->len_chanlist=32; /* XXX */ s->buf_change = ni_pcidio_change; + s->async_dma_dir = DMA_BIDIRECTIONAL; writel(0,devpriv->mite->daq_io_addr+Port_IO(0)); writel(0,devpriv->mite->daq_io_addr+Port_Pin_Directions(0)); diff --git a/comedi/drivers/ni_pcimio.c b/comedi/drivers/ni_pcimio.c index 3e873204..b1a38dee 100644 --- a/comedi/drivers/ni_pcimio.c +++ b/comedi/drivers/ni_pcimio.c @@ -1472,6 +1472,7 @@ static int pcimio_attach(comedi_device *dev,comedi_devconfig *it) printk(" error setting up mite\n"); return ret; } + comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev); if(boardtype.reg_type & ni_reg_m_series_mask) m_series_init_eeprom_buffer(dev); diff --git a/comedi/drivers/ni_stc.h b/comedi/drivers/ni_stc.h index 6390c384..86699b46 100644 --- a/comedi/drivers/ni_stc.h +++ b/comedi/drivers/ni_stc.h @@ -699,6 +699,14 @@ enum AI_AO_Select_Bits AO_DMA_Select_Mask = 0xf << AO_DMA_Select_Shift }; #define G0_G1_Select 0x0b +static inline unsigned ni_stc_dma_channel_select_bitfield(unsigned channel) +{ + if(channel < 4) return 1 << channel; + if(channel == 4) return 0x3; + if(channel == 5) return 0x5; + BUG(); + return 0; +} /* 16 bit registers */ diff --git a/include/linux/comedidev.h b/include/linux/comedidev.h index 8ad1e99e..eb1f6205 100644 --- a/include/linux/comedidev.h +++ b/include/linux/comedidev.h @@ -38,7 +38,7 @@ #include #include #include - +#include #include #include @@ -143,18 +143,27 @@ struct comedi_subdevice_struct{ void (*munge)( comedi_device *dev, comedi_subdevice *s, void *data, unsigned int num_bytes, unsigned int start_chan_index ); + enum dma_data_direction async_dma_dir; unsigned int state; struct class_device *class_dev; }; +struct comedi_buf_page +{ + void *virt_addr; + dma_addr_t dma_addr; +}; + struct comedi_async_struct{ comedi_subdevice *subdevice; void *prealloc_buf; /* pre-allocated buffer */ unsigned int prealloc_bufsz; /* buffer size, in bytes */ - unsigned long *buf_page_list; /* physical address of each page */ + struct comedi_buf_page *buf_page_list; /* virtual and dma address of each page */ + unsigned n_buf_pages; /* num elements in buf_page_list */ + unsigned int max_bufsize; /* maximum buffer size, bytes */ unsigned int mmap_count; /* current number of mmaps of prealloc_buf */ @@ -210,6 +219,9 @@ struct comedi_device_struct{ struct class_device *class_dev; unsigned minor; + /* hw_dev is passed to dma_alloc_coherent when allocating async buffers for subdevices + that have async_dma_dir set to something other than DMA_NONE */ + struct device *hw_dev; const char *board_name; const void * board_ptr; @@ -386,6 +398,7 @@ static inline int alloc_subdevices(comedi_device *dev, unsigned int num_subdevic for(i = 0; i < num_subdevices; ++i) { dev->subdevices[i].device = dev; + dev->subdevices[i].async_dma_dir = DMA_NONE; } return 0; } @@ -407,6 +420,19 @@ static inline unsigned int bytes_per_sample( const comedi_subdevice *subd ) return sizeof( sampl_t ); } +static inline void comedi_set_hw_dev(comedi_device *dev, struct device *hw_dev) +{ + if(dev->hw_dev) + { + put_device(dev->hw_dev); + } + dev->hw_dev = hw_dev; + if(dev->hw_dev) + { + get_device(dev->hw_dev); + } +} + int comedi_buf_put(comedi_async *async, sampl_t x); int comedi_buf_get(comedi_async *async, sampl_t *x); -- 2.26.2