]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 May 2013 16:46:45 +0000 (09:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 May 2013 16:46:45 +0000 (09:46 -0700)
Pull slave-dmaengine updates from Vinod Koul:
 "This time we have dmatest improvements from Andy along with dw_dmac
  fixes.  He has also done support for acpi for dmanegine.

  Also we have bunch of fixes going in DT support for dmanegine for
  various folks.  Then Haswell and other ioat changes from Dave and
  SUDMAC support from Shimoda."

* 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma: (53 commits)
  dma: tegra: implement suspend/resume callbacks
  dma:of: Use a mutex to protect the of_dma_list
  dma: of: Fix of_node reference leak
  dmaengine: sirf: move driver init from module_init to subsys_initcall
  sudmac: add support for SUDMAC
  dma: sh: add Kconfig
  at_hdmac: move to generic DMA binding
  ioatdma: ioat3_alloc_sed can be static
  ioatdma: Adding write back descriptor error status support for ioatdma 3.3
  ioatdma: S1200 platforms ioatdma channel 2 and 3 falsely advertise RAID cap
  ioatdma: Adding support for 16 src PQ ops and super extended descriptors
  ioatdma: Removing hw bug workaround for CB3.x .2 and earlier
  dw_dmac: add ACPI support
  dmaengine: call acpi_dma_request_slave_channel as well
  dma: acpi-dma: introduce ACPI DMA helpers
  dma: of: Remove unnecessary list_empty check
  DMA: OF: Check properties value before running be32_to_cpup() on it
  DMA: of: Constant names
  ioatdma: skip silicon bug workaround for pq_align for cb3.3
  ioatdma: Removing PQ val disable for cb3.3
  ...

1  2 
Documentation/acpi/enumeration.txt
drivers/dma/at_hdmac.c

index b0d541042ac61c7d5cf580d4fc37a486ea96521e,2874c904f3efe2654069930e2fca8471319cc3a2..d9be7a97dff35e7b1521e709e8a29c278d3fb434
@@@ -66,6 -66,83 +66,83 @@@ the ACPI device explicitly to acpi_plat
  drivers/acpi/acpi_platform.c. This limitation is only for the platform
  devices, SPI and I2C devices are created automatically as described below.
  
+ DMA support
+ ~~~~~~~~~~~
+ DMA controllers enumerated via ACPI should be registered in the system to
+ provide generic access to their resources. For example, a driver that would
+ like to be accessible to slave devices via generic API call
+ dma_request_slave_channel() must register itself at the end of the probe
+ function like this:
+       err = devm_acpi_dma_controller_register(dev, xlate_func, dw);
+       /* Handle the error if it's not a case of !CONFIG_ACPI */
+ and implement custom xlate function if needed (usually acpi_dma_simple_xlate()
+ is enough) which converts the FixedDMA resource provided by struct
+ acpi_dma_spec into the corresponding DMA channel. A piece of code for that case
+ could look like:
+       #ifdef CONFIG_ACPI
+       struct filter_args {
+               /* Provide necessary information for the filter_func */
+               ...
+       };
+       static bool filter_func(struct dma_chan *chan, void *param)
+       {
+               /* Choose the proper channel */
+               ...
+       }
+       static struct dma_chan *xlate_func(struct acpi_dma_spec *dma_spec,
+                       struct acpi_dma *adma)
+       {
+               dma_cap_mask_t cap;
+               struct filter_args args;
+               /* Prepare arguments for filter_func */
+               ...
+               return dma_request_channel(cap, filter_func, &args);
+       }
+       #else
+       static struct dma_chan *xlate_func(struct acpi_dma_spec *dma_spec,
+                       struct acpi_dma *adma)
+       {
+               return NULL;
+       }
+       #endif
+ dma_request_slave_channel() will call xlate_func() for each registered DMA
+ controller. In the xlate function the proper channel must be chosen based on
+ information in struct acpi_dma_spec and the properties of the controller
+ provided by struct acpi_dma.
+ Clients must call dma_request_slave_channel() with the string parameter that
+ corresponds to a specific FixedDMA resource. By default "tx" means the first
+ entry of the FixedDMA resource array, "rx" means the second entry. The table
+ below shows a layout:
+       Device (I2C0)
+       {
+               ...
+               Method (_CRS, 0, NotSerialized)
+               {
+                       Name (DBUF, ResourceTemplate ()
+                       {
+                               FixedDMA (0x0018, 0x0004, Width32bit, _Y48)
+                               FixedDMA (0x0019, 0x0005, Width32bit, )
+                       })
+               ...
+               }
+       }
+ So, the FixedDMA with request line 0x0018 is "tx" and next one is "rx" in
+ this example.
+ In robust cases the client unfortunately needs to call
+ acpi_dma_request_slave_chan_by_index() directly and therefore choose the
+ specific FixedDMA resource by its index.
  SPI serial bus support
  ~~~~~~~~~~~~~~~~~~~~~~
  Slave devices behind SPI bus have SpiSerialBus resource attached to them.
@@@ -199,8 -276,6 +276,8 @@@ the device to the driver. For example
        {
                Name (SBUF, ResourceTemplate()
                {
 +                      ...
 +                      // Used to power on/off the device
                        GpioIo (Exclusive, PullDefault, 0x0000, 0x0000,
                                IoRestrictionOutputOnly, "\\_SB.PCI0.GPI0",
                                0x00, ResourceConsumer,,)
                                // Pin List
                                0x0055
                        }
 +
 +                      // Interrupt for the device
 +                      GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone,
 +                               0x0000, "\\_SB.PCI0.GPI0", 0x00, ResourceConsumer,,)
 +                      {
 +                              // Pin list
 +                              0x0058
 +                      }
 +
                        ...
  
 -                      Return (SBUF)
                }
 +
 +              Return (SBUF)
        }
  
  These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0"
@@@ -232,24 -297,6 +309,24 @@@ The driver can do this by including <li
  acpi_get_gpio(path, gpio). This will return the Linux GPIO number or
  negative errno if there was no translation found.
  
 +In a simple case of just getting the Linux GPIO number from device
 +resources one can use acpi_get_gpio_by_index() helper function. It takes
 +pointer to the device and index of the GpioIo/GpioInt descriptor in the
 +device resources list. For example:
 +
 +      int gpio_irq, gpio_power;
 +      int ret;
 +
 +      gpio_irq = acpi_get_gpio_by_index(dev, 1, NULL);
 +      if (gpio_irq < 0)
 +              /* handle error */
 +
 +      gpio_power = acpi_get_gpio_by_index(dev, 0, NULL);
 +      if (gpio_power < 0)
 +              /* handle error */
 +
 +      /* Now we can use the GPIO numbers */
 +
  Other GpioIo parameters must be converted first by the driver to be
  suitable to the gpiolib before passing them.
  
diff --combined drivers/dma/at_hdmac.c
index 88cfc61329d20fb137a46ff852569bf8b567c60a,3502c412caf90dc654ed86ca79819739a1a54b87..e923cda930f98a09c90fa73b868cfeb3b619b30d
@@@ -24,6 -24,7 +24,7 @@@
  #include <linux/slab.h>
  #include <linux/of.h>
  #include <linux/of_device.h>
+ #include <linux/of_dma.h>
  
  #include "at_hdmac_regs.h"
  #include "dmaengine.h"
@@@ -310,6 -311,8 +311,6 @@@ static void atc_complete_all(struct at_
  
        dev_vdbg(chan2dev(&atchan->chan_common), "complete all\n");
  
 -      BUG_ON(atc_chan_is_enabled(atchan));
 -
        /*
         * Submit queued descriptors ASAP, i.e. before we go through
         * the completed ones.
@@@ -366,9 -369,6 +367,9 @@@ static void atc_advance_work(struct at_
  {
        dev_vdbg(chan2dev(&atchan->chan_common), "advance_work\n");
  
 +      if (atc_chan_is_enabled(atchan))
 +              return;
 +
        if (list_empty(&atchan->active_list) ||
            list_is_singular(&atchan->active_list)) {
                atc_complete_all(atchan);
@@@ -677,7 -677,7 +678,7 @@@ atc_prep_slave_sg(struct dma_chan *chan
                ctrlb |=  ATC_DST_ADDR_MODE_FIXED
                        | ATC_SRC_ADDR_MODE_INCR
                        | ATC_FC_MEM2PER
-                       | ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF);
+                       | ATC_SIF(atchan->mem_if) | ATC_DIF(atchan->per_if);
                reg = sconfig->dst_addr;
                for_each_sg(sgl, sg, sg_len, i) {
                        struct at_desc  *desc;
                ctrlb |=  ATC_DST_ADDR_MODE_INCR
                        | ATC_SRC_ADDR_MODE_FIXED
                        | ATC_FC_PER2MEM
-                       | ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF);
+                       | ATC_SIF(atchan->per_if) | ATC_DIF(atchan->mem_if);
  
                reg = sconfig->src_addr;
                for_each_sg(sgl, sg, sg_len, i) {
@@@ -822,8 -822,8 +823,8 @@@ atc_dma_cyclic_fill_desc(struct dma_cha
                desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED
                                | ATC_SRC_ADDR_MODE_INCR
                                | ATC_FC_MEM2PER
-                               | ATC_SIF(AT_DMA_MEM_IF)
-                               | ATC_DIF(AT_DMA_PER_IF);
+                               | ATC_SIF(atchan->mem_if)
+                               | ATC_DIF(atchan->per_if);
                break;
  
        case DMA_DEV_TO_MEM:
                desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR
                                | ATC_SRC_ADDR_MODE_FIXED
                                | ATC_FC_PER2MEM
-                               | ATC_SIF(AT_DMA_PER_IF)
-                               | ATC_DIF(AT_DMA_MEM_IF);
+                               | ATC_SIF(atchan->per_if)
+                               | ATC_DIF(atchan->mem_if);
                break;
  
        default:
@@@ -1079,7 -1079,9 +1080,7 @@@ static void atc_issue_pending(struct dm
                return;
  
        spin_lock_irqsave(&atchan->lock, flags);
 -      if (!atc_chan_is_enabled(atchan)) {
 -              atc_advance_work(atchan);
 -      }
 +      atc_advance_work(atchan);
        spin_unlock_irqrestore(&atchan->lock, flags);
  }
  
@@@ -1188,6 -1190,67 +1189,67 @@@ static void atc_free_chan_resources(str
        dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
  }
  
+ #ifdef CONFIG_OF
+ static bool at_dma_filter(struct dma_chan *chan, void *slave)
+ {
+       struct at_dma_slave *atslave = slave;
+       if (atslave->dma_dev == chan->device->dev) {
+               chan->private = atslave;
+               return true;
+       } else {
+               return false;
+       }
+ }
+ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
+                                    struct of_dma *of_dma)
+ {
+       struct dma_chan *chan;
+       struct at_dma_chan *atchan;
+       struct at_dma_slave *atslave;
+       dma_cap_mask_t mask;
+       unsigned int per_id;
+       struct platform_device *dmac_pdev;
+       if (dma_spec->args_count != 2)
+               return NULL;
+       dmac_pdev = of_find_device_by_node(dma_spec->np);
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL);
+       if (!atslave)
+               return NULL;
+       /*
+        * We can fill both SRC_PER and DST_PER, one of these fields will be
+        * ignored depending on DMA transfer direction.
+        */
+       per_id = dma_spec->args[1];
+       atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW
+                     | ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id)
+                     | ATC_SRC_PER(per_id);
+       atslave->dma_dev = &dmac_pdev->dev;
+       chan = dma_request_channel(mask, at_dma_filter, atslave);
+       if (!chan)
+               return NULL;
+       atchan = to_at_dma_chan(chan);
+       atchan->per_if = dma_spec->args[0] & 0xff;
+       atchan->mem_if = (dma_spec->args[0] >> 16) & 0xff;
+       return chan;
+ }
+ #else
+ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
+                                    struct of_dma *of_dma)
+ {
+       return NULL;
+ }
+ #endif
  
  /*--  Module Management  -----------------------------------------------*/
  
@@@ -1342,6 -1405,8 +1404,8 @@@ static int __init at_dma_probe(struct p
        for (i = 0; i < plat_dat->nr_channels; i++) {
                struct at_dma_chan      *atchan = &atdma->chan[i];
  
+               atchan->mem_if = AT_DMA_MEM_IF;
+               atchan->per_if = AT_DMA_PER_IF;
                atchan->chan_common.device = &atdma->dma_common;
                dma_cookie_init(&atchan->chan_common);
                list_add_tail(&atchan->chan_common.device_node,
  
        dma_async_device_register(&atdma->dma_common);
  
+       /*
+        * Do not return an error if the dmac node is not present in order to
+        * not break the existing way of requesting channel with
+        * dma_request_channel().
+        */
+       if (pdev->dev.of_node) {
+               err = of_dma_controller_register(pdev->dev.of_node,
+                                                at_dma_xlate, atdma);
+               if (err) {
+                       dev_err(&pdev->dev, "could not register of_dma_controller\n");
+                       goto err_of_dma_controller_register;
+               }
+       }
        return 0;
  
+ err_of_dma_controller_register:
+       dma_async_device_unregister(&atdma->dma_common);
+       dma_pool_destroy(atdma->dma_desc_pool);
  err_pool_create:
        platform_set_drvdata(pdev, NULL);
        free_irq(platform_get_irq(pdev, 0), atdma);
@@@ -1406,7 -1488,7 +1487,7 @@@ err_kfree
        return err;
  }
  
- static int __exit at_dma_remove(struct platform_device *pdev)
+ static int at_dma_remove(struct platform_device *pdev)
  {
        struct at_dma           *atdma = platform_get_drvdata(pdev);
        struct dma_chan         *chan, *_chan;
@@@ -1564,7 -1646,7 +1645,7 @@@ static const struct dev_pm_ops at_dma_d
  };
  
  static struct platform_driver at_dma_driver = {
-       .remove         = __exit_p(at_dma_remove),
+       .remove         = at_dma_remove,
        .shutdown       = at_dma_shutdown,
        .id_table       = atdma_devtypes,
        .driver = {