LCOV - code coverage report
Current view: top level - drivers/dma - bcm2708-dmaengine.c (source / functions) Hit Total Coverage
Test: Real Lines: 46 79 58.2 %
Date: 2020-10-17 15:46:16 Functions: 0 10 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * BCM2708 legacy DMA API
       3                 :            :  *
       4                 :            :  * This program is free software; you can redistribute it and/or modify
       5                 :            :  * it under the terms of the GNU General Public License as published by
       6                 :            :  * the Free Software Foundation; either version 2 of the License, or
       7                 :            :  * (at your option) any later version.
       8                 :            :  *
       9                 :            :  * This program is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU General Public License for more details.
      13                 :            :  */
      14                 :            : 
      15                 :            : #include <linux/init.h>
      16                 :            : #include <linux/interrupt.h>
      17                 :            : #include <linux/list.h>
      18                 :            : #include <linux/module.h>
      19                 :            : #include <linux/platform_data/dma-bcm2708.h>
      20                 :            : #include <linux/platform_device.h>
      21                 :            : #include <linux/slab.h>
      22                 :            : #include <linux/io.h>
      23                 :            : #include <linux/spinlock.h>
      24                 :            : 
      25                 :            : #include "virt-dma.h"
      26                 :            : 
      27                 :            : #define CACHE_LINE_MASK 31
      28                 :            : #define DEFAULT_DMACHAN_BITMAP 0x10  /* channel 4 only */
      29                 :            : 
      30                 :            : /* valid only for channels 0 - 14, 15 has its own base address */
      31                 :            : #define BCM2708_DMA_CHAN(n)     ((n) << 8) /* base address */
      32                 :            : #define BCM2708_DMA_CHANIO(dma_base, n) \
      33                 :            :         ((void __iomem *)((char *)(dma_base) + BCM2708_DMA_CHAN(n)))
      34                 :            : 
      35                 :            : struct vc_dmaman {
      36                 :            :         void __iomem *dma_base;
      37                 :            :         u32 chan_available; /* bitmap of available channels */
      38                 :            :         u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */
      39                 :            :         struct mutex lock;
      40                 :            : };
      41                 :            : 
      42                 :            : static struct device *dmaman_dev;       /* we assume there's only one! */
      43                 :            : static struct vc_dmaman *g_dmaman;      /* DMA manager */
      44                 :            : 
      45                 :            : /* DMA Auxiliary Functions */
      46                 :            : 
      47                 :            : /* A DMA buffer on an arbitrary boundary may separate a cache line into a
      48                 :            :    section inside the DMA buffer and another section outside it.
      49                 :            :    Even if we flush DMA buffers from the cache there is always the chance that
      50                 :            :    during a DMA someone will access the part of a cache line that is outside
      51                 :            :    the DMA buffer - which will then bring in unwelcome data.
      52                 :            :    Without being able to dictate our own buffer pools we must insist that
      53                 :            :    DMA buffers consist of a whole number of cache lines.
      54                 :            : */
      55                 :          0 : extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len)
      56                 :            : {
      57                 :            :         int i;
      58                 :            : 
      59                 :          0 :         for (i = 0; i < sg_len; i++) {
      60                 :          0 :                 if (sg_ptr[i].offset & CACHE_LINE_MASK ||
      61                 :          0 :                     sg_ptr[i].length & CACHE_LINE_MASK)
      62                 :            :                         return 0;
      63                 :            :         }
      64                 :            : 
      65                 :            :         return 1;
      66                 :            : }
      67                 :            : EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma);
      68                 :            : 
      69                 :          3 : extern void bcm_dma_start(void __iomem *dma_chan_base,
      70                 :            :                           dma_addr_t control_block)
      71                 :            : {
      72                 :          3 :         dsb(sy);        /* ARM data synchronization (push) operation */
      73                 :            : 
      74                 :          3 :         writel(control_block, dma_chan_base + BCM2708_DMA_ADDR);
      75                 :          3 :         writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS);
      76                 :          3 : }
      77                 :            : EXPORT_SYMBOL_GPL(bcm_dma_start);
      78                 :            : 
      79                 :          3 : extern void bcm_dma_wait_idle(void __iomem *dma_chan_base)
      80                 :            : {
      81                 :          3 :         dsb(sy);
      82                 :            : 
      83                 :            :         /* ugly busy wait only option for now */
      84                 :          3 :         while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE)
      85                 :          2 :                 cpu_relax();
      86                 :          3 : }
      87                 :            : EXPORT_SYMBOL_GPL(bcm_dma_wait_idle);
      88                 :            : 
      89                 :          0 : extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
      90                 :            : {
      91                 :          0 :         dsb(sy);
      92                 :            : 
      93                 :          0 :         return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
      94                 :            : }
      95                 :            : EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
      96                 :            : 
      97                 :            : /* Complete an ongoing DMA (assuming its results are to be ignored)
      98                 :            :    Does nothing if there is no DMA in progress.
      99                 :            :    This routine waits for the current AXI transfer to complete before
     100                 :            :    terminating the current DMA. If the current transfer is hung on a DREQ used
     101                 :            :    by an uncooperative peripheral the AXI transfer may never complete.  In this
     102                 :            :    case the routine times out and return a non-zero error code.
     103                 :            :    Use of this routine doesn't guarantee that the ongoing or aborted DMA
     104                 :            :    does not produce an interrupt.
     105                 :            : */
     106                 :          0 : extern int bcm_dma_abort(void __iomem *dma_chan_base)
     107                 :            : {
     108                 :            :         unsigned long int cs;
     109                 :            :         int rc = 0;
     110                 :            : 
     111                 :          0 :         cs = readl(dma_chan_base + BCM2708_DMA_CS);
     112                 :            : 
     113                 :          0 :         if (BCM2708_DMA_ACTIVE & cs) {
     114                 :            :                 long int timeout = 10000;
     115                 :            : 
     116                 :            :                 /* write 0 to the active bit - pause the DMA */
     117                 :          0 :                 writel(0, dma_chan_base + BCM2708_DMA_CS);
     118                 :            : 
     119                 :            :                 /* wait for any current AXI transfer to complete */
     120                 :          0 :                 while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0)
     121                 :          0 :                         cs = readl(dma_chan_base + BCM2708_DMA_CS);
     122                 :            : 
     123                 :          0 :                 if (0 != (cs & BCM2708_DMA_ISPAUSED)) {
     124                 :            :                         /* we'll un-pause when we set of our next DMA */
     125                 :            :                         rc = -ETIMEDOUT;
     126                 :            : 
     127                 :          0 :                 } else if (BCM2708_DMA_ACTIVE & cs) {
     128                 :            :                         /* terminate the control block chain */
     129                 :          0 :                         writel(0, dma_chan_base + BCM2708_DMA_NEXTCB);
     130                 :            : 
     131                 :            :                         /* abort the whole DMA */
     132                 :          0 :                         writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE,
     133                 :            :                                dma_chan_base + BCM2708_DMA_CS);
     134                 :            :                 }
     135                 :            :         }
     136                 :            : 
     137                 :          0 :         return rc;
     138                 :            : }
     139                 :            : EXPORT_SYMBOL_GPL(bcm_dma_abort);
     140                 :            : 
     141                 :            :  /* DMA Manager Device Methods */
     142                 :            : 
     143                 :            : static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base,
     144                 :            :                            u32 chans_available)
     145                 :            : {
     146                 :          3 :         dmaman->dma_base = dma_base;
     147                 :          3 :         dmaman->chan_available = chans_available;
     148                 :          3 :         dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c;  /* 2 & 3 */
     149                 :          3 :         dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01;  /* 0 */
     150                 :          3 :         dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe;  /* 1 to 7 */
     151                 :          3 :         dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00;  /* 8 to 14 */
     152                 :            : }
     153                 :            : 
     154                 :          3 : static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman,
     155                 :            :                                 unsigned required_feature_set)
     156                 :            : {
     157                 :            :         u32 chans;
     158                 :            :         int chan = 0;
     159                 :            :         int feature;
     160                 :            : 
     161                 :          3 :         chans = dmaman->chan_available;
     162                 :          3 :         for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++)
     163                 :            :                 /* select the subset of available channels with the desired
     164                 :            :                    features */
     165                 :          3 :                 if (required_feature_set & (1 << feature))
     166                 :          3 :                         chans &= dmaman->has_feature[feature];
     167                 :            : 
     168                 :          3 :         if (!chans)
     169                 :            :                 return -ENOENT;
     170                 :            : 
     171                 :            :         /* return the ordinal of the first channel in the bitmap */
     172                 :          3 :         while (chans != 0 && (chans & 1) == 0) {
     173                 :          0 :                 chans >>= 1;
     174                 :          0 :                 chan++;
     175                 :            :         }
     176                 :            :         /* claim the channel */
     177                 :          3 :         dmaman->chan_available &= ~(1 << chan);
     178                 :            : 
     179                 :          3 :         return chan;
     180                 :            : }
     181                 :            : 
     182                 :            : static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan)
     183                 :            : {
     184                 :          0 :         if (chan < 0)
     185                 :            :                 return -EINVAL;
     186                 :            : 
     187                 :          0 :         if ((1 << chan) & dmaman->chan_available)
     188                 :            :                 return -EIDRM;
     189                 :            : 
     190                 :          0 :         dmaman->chan_available |= (1 << chan);
     191                 :            : 
     192                 :            :         return 0;
     193                 :            : }
     194                 :            : 
     195                 :            : /* DMA Manager Monitor */
     196                 :            : 
     197                 :          3 : extern int bcm_dma_chan_alloc(unsigned required_feature_set,
     198                 :            :                               void __iomem **out_dma_base, int *out_dma_irq)
     199                 :            : {
     200                 :          3 :         struct vc_dmaman *dmaman = g_dmaman;
     201                 :          3 :         struct platform_device *pdev = to_platform_device(dmaman_dev);
     202                 :            :         struct resource *r;
     203                 :            :         int chan;
     204                 :            : 
     205                 :          3 :         if (!dmaman_dev)
     206                 :            :                 return -ENODEV;
     207                 :            : 
     208                 :          3 :         mutex_lock(&dmaman->lock);
     209                 :          3 :         chan = vc_dmaman_chan_alloc(dmaman, required_feature_set);
     210                 :          3 :         if (chan < 0)
     211                 :            :                 goto out;
     212                 :            : 
     213                 :          3 :         r = platform_get_resource(pdev, IORESOURCE_IRQ, (unsigned int)chan);
     214                 :          3 :         if (!r) {
     215                 :          0 :                 dev_err(dmaman_dev, "failed to get irq for DMA channel %d\n",
     216                 :            :                         chan);
     217                 :            :                 vc_dmaman_chan_free(dmaman, chan);
     218                 :            :                 chan = -ENOENT;
     219                 :            :                 goto out;
     220                 :            :         }
     221                 :            : 
     222                 :          3 :         *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, chan);
     223                 :          3 :         *out_dma_irq = r->start;
     224                 :            :         dev_dbg(dmaman_dev,
     225                 :            :                 "Legacy API allocated channel=%d, base=%p, irq=%i\n",
     226                 :            :                 chan, *out_dma_base, *out_dma_irq);
     227                 :            : 
     228                 :            : out:
     229                 :          3 :         mutex_unlock(&dmaman->lock);
     230                 :            : 
     231                 :          3 :         return chan;
     232                 :            : }
     233                 :            : EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc);
     234                 :            : 
     235                 :          0 : extern int bcm_dma_chan_free(int channel)
     236                 :            : {
     237                 :          0 :         struct vc_dmaman *dmaman = g_dmaman;
     238                 :            :         int rc;
     239                 :            : 
     240                 :          0 :         if (!dmaman_dev)
     241                 :            :                 return -ENODEV;
     242                 :            : 
     243                 :          0 :         mutex_lock(&dmaman->lock);
     244                 :            :         rc = vc_dmaman_chan_free(dmaman, channel);
     245                 :          0 :         mutex_unlock(&dmaman->lock);
     246                 :            : 
     247                 :          0 :         return rc;
     248                 :            : }
     249                 :            : EXPORT_SYMBOL_GPL(bcm_dma_chan_free);
     250                 :            : 
     251                 :          3 : int bcm_dmaman_probe(struct platform_device *pdev, void __iomem *base,
     252                 :            :                      u32 chans_available)
     253                 :            : {
     254                 :          3 :         struct device *dev = &pdev->dev;
     255                 :            :         struct vc_dmaman *dmaman;
     256                 :            : 
     257                 :            :         dmaman = devm_kzalloc(dev, sizeof(*dmaman), GFP_KERNEL);
     258                 :          3 :         if (!dmaman)
     259                 :            :                 return -ENOMEM;
     260                 :            : 
     261                 :          3 :         mutex_init(&dmaman->lock);
     262                 :            :         vc_dmaman_init(dmaman, base, chans_available);
     263                 :          3 :         g_dmaman = dmaman;
     264                 :          3 :         dmaman_dev = dev;
     265                 :            : 
     266                 :          3 :         dev_info(dev, "DMA legacy API manager, dmachans=0x%x\n",
     267                 :            :                  chans_available);
     268                 :            : 
     269                 :          3 :         return 0;
     270                 :            : }
     271                 :            : EXPORT_SYMBOL(bcm_dmaman_probe);
     272                 :            : 
     273                 :          0 : int bcm_dmaman_remove(struct platform_device *pdev)
     274                 :            : {
     275                 :          0 :         dmaman_dev = NULL;
     276                 :            : 
     277                 :          0 :         return 0;
     278                 :            : }
     279                 :            : EXPORT_SYMBOL(bcm_dmaman_remove);
     280                 :            : 
     281                 :            : MODULE_LICENSE("GPL");
    

Generated by: LCOV version 1.14