These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / dma / imx-sdma.c
index 62bbd79..0f6fd42 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/dmaengine.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_dma.h>
 
 #include <asm/irq.h>
 #include <linux/platform_data/dma-imx-sdma.h>
 #include <linux/platform_data/dma-imx.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 
 #include "dmaengine.h"
 
  */
 #define CHANGE_ENDIANNESS   0x80
 
+/*
+ *  p_2_p watermark_level description
+ *     Bits            Name                    Description
+ *     0-7             Lower WML               Lower watermark level
+ *     8               PS                      1: Pad Swallowing
+ *                                             0: No Pad Swallowing
+ *     9               PA                      1: Pad Adding
+ *                                             0: No Pad Adding
+ *     10              SPDIF                   If this bit is set both source
+ *                                             and destination are on SPBA
+ *     11              Source Bit(SP)          1: Source on SPBA
+ *                                             0: Source on AIPS
+ *     12              Destination Bit(DP)     1: Destination on SPBA
+ *                                             0: Destination on AIPS
+ *     13-15           ---------               MUST BE 0
+ *     16-23           Higher WML              HWML
+ *     24-27           N                       Total number of samples after
+ *                                             which Pad adding/Swallowing
+ *                                             must be done. It must be odd.
+ *     28              Lower WML Event(LWE)    SDMA events reg to check for
+ *                                             LWML event mask
+ *                                             0: LWE in EVENTS register
+ *                                             1: LWE in EVENTS2 register
+ *     29              Higher WML Event(HWE)   SDMA events reg to check for
+ *                                             HWML event mask
+ *                                             0: HWE in EVENTS register
+ *                                             1: HWE in EVENTS2 register
+ *     30              ---------               MUST BE 0
+ *     31              CONT                    1: Amount of samples to be
+ *                                             transferred is unknown and
+ *                                             script will keep on
+ *                                             transferring samples as long as
+ *                                             both events are detected and
+ *                                             script must be manually stopped
+ *                                             by the application
+ *                                             0: The amount of samples to be
+ *                                             transferred is equal to the
+ *                                             count field of mode word
+ */
+#define SDMA_WATERMARK_LEVEL_LWML      0xFF
+#define SDMA_WATERMARK_LEVEL_PS                BIT(8)
+#define SDMA_WATERMARK_LEVEL_PA                BIT(9)
+#define SDMA_WATERMARK_LEVEL_SPDIF     BIT(10)
+#define SDMA_WATERMARK_LEVEL_SP                BIT(11)
+#define SDMA_WATERMARK_LEVEL_DP                BIT(12)
+#define SDMA_WATERMARK_LEVEL_HWML      (0xFF << 16)
+#define SDMA_WATERMARK_LEVEL_LWE       BIT(28)
+#define SDMA_WATERMARK_LEVEL_HWE       BIT(29)
+#define SDMA_WATERMARK_LEVEL_CONT      BIT(31)
+
 /*
  * Mode/Count of data node descriptors - IPCv2
  */
@@ -259,8 +313,9 @@ struct sdma_channel {
        struct sdma_buffer_descriptor   *bd;
        dma_addr_t                      bd_phys;
        unsigned int                    pc_from_device, pc_to_device;
+       unsigned int                    device_to_device;
        unsigned long                   flags;
-       dma_addr_t                      per_address;
+       dma_addr_t                      per_address, per_address2;
        unsigned long                   event_mask[2];
        unsigned long                   watermark_level;
        u32                             shp_addr, per_addr;
@@ -328,6 +383,8 @@ struct sdma_engine {
        u32                             script_number;
        struct sdma_script_start_addrs  *script_addrs;
        const struct sdma_driver_data   *drvdata;
+       u32                             spba_start_addr;
+       u32                             spba_end_addr;
 };
 
 static struct sdma_driver_data sdma_imx31 = {
@@ -420,7 +477,7 @@ static struct sdma_driver_data sdma_imx6q = {
        .script_addrs = &sdma_script_imx6q,
 };
 
-static struct platform_device_id sdma_devtypes[] = {
+static const struct platform_device_id sdma_devtypes[] = {
        {
                .name = "imx25-sdma",
                .driver_data = (unsigned long)&sdma_imx25,
@@ -705,6 +762,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
 
        sdmac->pc_from_device = 0;
        sdmac->pc_to_device = 0;
+       sdmac->device_to_device = 0;
 
        switch (peripheral_type) {
        case IMX_DMATYPE_MEMORY:
@@ -780,6 +838,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
 
        sdmac->pc_from_device = per_2_emi;
        sdmac->pc_to_device = emi_2_per;
+       sdmac->device_to_device = per_2_per;
 }
 
 static int sdma_load_context(struct sdma_channel *sdmac)
@@ -792,11 +851,12 @@ static int sdma_load_context(struct sdma_channel *sdmac)
        int ret;
        unsigned long flags;
 
-       if (sdmac->direction == DMA_DEV_TO_MEM) {
+       if (sdmac->direction == DMA_DEV_TO_MEM)
                load_address = sdmac->pc_from_device;
-       } else {
+       else if (sdmac->direction == DMA_DEV_TO_DEV)
+               load_address = sdmac->device_to_device;
+       else
                load_address = sdmac->pc_to_device;
-       }
 
        if (load_address < 0)
                return load_address;
@@ -851,6 +911,46 @@ static int sdma_disable_channel(struct dma_chan *chan)
        return 0;
 }
 
+static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
+{
+       struct sdma_engine *sdma = sdmac->sdma;
+
+       int lwml = sdmac->watermark_level & SDMA_WATERMARK_LEVEL_LWML;
+       int hwml = (sdmac->watermark_level & SDMA_WATERMARK_LEVEL_HWML) >> 16;
+
+       set_bit(sdmac->event_id0 % 32, &sdmac->event_mask[1]);
+       set_bit(sdmac->event_id1 % 32, &sdmac->event_mask[0]);
+
+       if (sdmac->event_id0 > 31)
+               sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_LWE;
+
+       if (sdmac->event_id1 > 31)
+               sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_HWE;
+
+       /*
+        * If LWML(src_maxburst) > HWML(dst_maxburst), we need
+        * swap LWML and HWML of INFO(A.3.2.5.1), also need swap
+        * r0(event_mask[1]) and r1(event_mask[0]).
+        */
+       if (lwml > hwml) {
+               sdmac->watermark_level &= ~(SDMA_WATERMARK_LEVEL_LWML |
+                                               SDMA_WATERMARK_LEVEL_HWML);
+               sdmac->watermark_level |= hwml;
+               sdmac->watermark_level |= lwml << 16;
+               swap(sdmac->event_mask[0], sdmac->event_mask[1]);
+       }
+
+       if (sdmac->per_address2 >= sdma->spba_start_addr &&
+                       sdmac->per_address2 <= sdma->spba_end_addr)
+               sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SP;
+
+       if (sdmac->per_address >= sdma->spba_start_addr &&
+                       sdmac->per_address <= sdma->spba_end_addr)
+               sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_DP;
+
+       sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT;
+}
+
 static int sdma_config_channel(struct dma_chan *chan)
 {
        struct sdma_channel *sdmac = to_sdma_chan(chan);
@@ -869,6 +969,12 @@ static int sdma_config_channel(struct dma_chan *chan)
                sdma_event_enable(sdmac, sdmac->event_id0);
        }
 
+       if (sdmac->event_id1) {
+               if (sdmac->event_id1 >= sdmac->sdma->drvdata->num_events)
+                       return -EINVAL;
+               sdma_event_enable(sdmac, sdmac->event_id1);
+       }
+
        switch (sdmac->peripheral_type) {
        case IMX_DMATYPE_DSP:
                sdma_config_ownership(sdmac, false, true, true);
@@ -887,19 +993,17 @@ static int sdma_config_channel(struct dma_chan *chan)
                        (sdmac->peripheral_type != IMX_DMATYPE_DSP)) {
                /* Handle multiple event channels differently */
                if (sdmac->event_id1) {
-                       sdmac->event_mask[1] = BIT(sdmac->event_id1 % 32);
-                       if (sdmac->event_id1 > 31)
-                               __set_bit(31, &sdmac->watermark_level);
-                       sdmac->event_mask[0] = BIT(sdmac->event_id0 % 32);
-                       if (sdmac->event_id0 > 31)
-                               __set_bit(30, &sdmac->watermark_level);
-               } else {
+                       if (sdmac->peripheral_type == IMX_DMATYPE_ASRC_SP ||
+                           sdmac->peripheral_type == IMX_DMATYPE_ASRC)
+                               sdma_set_watermarklevel_for_p2p(sdmac);
+               } else
                        __set_bit(sdmac->event_id0, sdmac->event_mask);
-               }
+
                /* Watermark Level */
                sdmac->watermark_level |= sdmac->watermark_level;
                /* Address */
                sdmac->shp_addr = sdmac->per_address;
+               sdmac->per_addr = sdmac->per_address2;
        } else {
                sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */
        }
@@ -987,17 +1091,22 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
 
        sdmac->peripheral_type = data->peripheral_type;
        sdmac->event_id0 = data->dma_request;
+       sdmac->event_id1 = data->dma_request2;
 
-       clk_enable(sdmac->sdma->clk_ipg);
-       clk_enable(sdmac->sdma->clk_ahb);
+       ret = clk_enable(sdmac->sdma->clk_ipg);
+       if (ret)
+               return ret;
+       ret = clk_enable(sdmac->sdma->clk_ahb);
+       if (ret)
+               goto disable_clk_ipg;
 
        ret = sdma_request_channel(sdmac);
        if (ret)
-               return ret;
+               goto disable_clk_ahb;
 
        ret = sdma_set_channel_priority(sdmac, prio);
        if (ret)
-               return ret;
+               goto disable_clk_ahb;
 
        dma_async_tx_descriptor_init(&sdmac->desc, chan);
        sdmac->desc.tx_submit = sdma_tx_submit;
@@ -1005,6 +1114,12 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
        sdmac->desc.flags = DMA_CTRL_ACK;
 
        return 0;
+
+disable_clk_ahb:
+       clk_disable(sdmac->sdma->clk_ahb);
+disable_clk_ipg:
+       clk_disable(sdmac->sdma->clk_ipg);
+       return ret;
 }
 
 static void sdma_free_chan_resources(struct dma_chan *chan)
@@ -1221,6 +1336,14 @@ static int sdma_config(struct dma_chan *chan,
                sdmac->watermark_level = dmaengine_cfg->src_maxburst *
                        dmaengine_cfg->src_addr_width;
                sdmac->word_size = dmaengine_cfg->src_addr_width;
+       } else if (dmaengine_cfg->direction == DMA_DEV_TO_DEV) {
+               sdmac->per_address2 = dmaengine_cfg->src_addr;
+               sdmac->per_address = dmaengine_cfg->dst_addr;
+               sdmac->watermark_level = dmaengine_cfg->src_maxburst &
+                       SDMA_WATERMARK_LEVEL_LWML;
+               sdmac->watermark_level |= (dmaengine_cfg->dst_maxburst << 16) &
+                       SDMA_WATERMARK_LEVEL_HWML;
+               sdmac->word_size = dmaengine_cfg->dst_addr_width;
        } else {
                sdmac->per_address = dmaengine_cfg->dst_addr;
                sdmac->watermark_level = dmaengine_cfg->dst_maxburst *
@@ -1337,6 +1460,72 @@ err_firmware:
        release_firmware(fw);
 }
 
+#define EVENT_REMAP_CELLS 3
+
+static int sdma_event_remap(struct sdma_engine *sdma)
+{
+       struct device_node *np = sdma->dev->of_node;
+       struct device_node *gpr_np = of_parse_phandle(np, "gpr", 0);
+       struct property *event_remap;
+       struct regmap *gpr;
+       char propname[] = "fsl,sdma-event-remap";
+       u32 reg, val, shift, num_map, i;
+       int ret = 0;
+
+       if (IS_ERR(np) || IS_ERR(gpr_np))
+               goto out;
+
+       event_remap = of_find_property(np, propname, NULL);
+       num_map = event_remap ? (event_remap->length / sizeof(u32)) : 0;
+       if (!num_map) {
+               dev_dbg(sdma->dev, "no event needs to be remapped\n");
+               goto out;
+       } else if (num_map % EVENT_REMAP_CELLS) {
+               dev_err(sdma->dev, "the property %s must modulo %d\n",
+                               propname, EVENT_REMAP_CELLS);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       gpr = syscon_node_to_regmap(gpr_np);
+       if (IS_ERR(gpr)) {
+               dev_err(sdma->dev, "failed to get gpr regmap\n");
+               ret = PTR_ERR(gpr);
+               goto out;
+       }
+
+       for (i = 0; i < num_map; i += EVENT_REMAP_CELLS) {
+               ret = of_property_read_u32_index(np, propname, i, &reg);
+               if (ret) {
+                       dev_err(sdma->dev, "failed to read property %s index %d\n",
+                                       propname, i);
+                       goto out;
+               }
+
+               ret = of_property_read_u32_index(np, propname, i + 1, &shift);
+               if (ret) {
+                       dev_err(sdma->dev, "failed to read property %s index %d\n",
+                                       propname, i + 1);
+                       goto out;
+               }
+
+               ret = of_property_read_u32_index(np, propname, i + 2, &val);
+               if (ret) {
+                       dev_err(sdma->dev, "failed to read property %s index %d\n",
+                                       propname, i + 2);
+                       goto out;
+               }
+
+               regmap_update_bits(gpr, reg, BIT(shift), val << shift);
+       }
+
+out:
+       if (!IS_ERR(gpr_np))
+               of_node_put(gpr_np);
+
+       return ret;
+}
+
 static int sdma_get_firmware(struct sdma_engine *sdma,
                const char *fw_name)
 {
@@ -1354,8 +1543,12 @@ static int sdma_init(struct sdma_engine *sdma)
        int i, ret;
        dma_addr_t ccb_phys;
 
-       clk_enable(sdma->clk_ipg);
-       clk_enable(sdma->clk_ahb);
+       ret = clk_enable(sdma->clk_ipg);
+       if (ret)
+               return ret;
+       ret = clk_enable(sdma->clk_ahb);
+       if (ret)
+               goto disable_clk_ipg;
 
        /* Be sure SDMA has not started yet */
        writel_relaxed(0, sdma->regs + SDMA_H_C0PTR);
@@ -1411,8 +1604,9 @@ static int sdma_init(struct sdma_engine *sdma)
        return 0;
 
 err_dma_alloc:
-       clk_disable(sdma->clk_ipg);
        clk_disable(sdma->clk_ahb);
+disable_clk_ipg:
+       clk_disable(sdma->clk_ipg);
        dev_err(sdma->dev, "initialisation failed with %d\n", ret);
        return ret;
 }
@@ -1444,6 +1638,14 @@ static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec,
        data.dma_request = dma_spec->args[0];
        data.peripheral_type = dma_spec->args[1];
        data.priority = dma_spec->args[2];
+       /*
+        * init dma_request2 to zero, which is not used by the dts.
+        * For P2P, dma_request2 is init from dma_request_channel(),
+        * chan->private will point to the imx_dma_data, and in
+        * device_alloc_chan_resources(), imx_dma_data.dma_request2 will
+        * be set to sdmac->event_id1.
+        */
+       data.dma_request2 = 0;
 
        return dma_request_channel(mask, sdma_filter_fn, &data);
 }
@@ -1453,10 +1655,12 @@ static int sdma_probe(struct platform_device *pdev)
        const struct of_device_id *of_id =
                        of_match_device(sdma_dt_ids, &pdev->dev);
        struct device_node *np = pdev->dev.of_node;
+       struct device_node *spba_bus;
        const char *fw_name;
        int ret;
        int irq;
        struct resource *iores;
+       struct resource spba_res;
        struct sdma_platform_data *pdata = dev_get_platdata(&pdev->dev);
        int i;
        struct sdma_engine *sdma;
@@ -1551,6 +1755,10 @@ static int sdma_probe(struct platform_device *pdev)
        if (ret)
                goto err_init;
 
+       ret = sdma_event_remap(sdma);
+       if (ret)
+               goto err_init;
+
        if (sdma->drvdata->script_addrs)
                sdma_add_scripts(sdma, sdma->drvdata->script_addrs);
        if (pdata && pdata->script_addrs)
@@ -1608,9 +1816,15 @@ static int sdma_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "failed to register controller\n");
                        goto err_register;
                }
-       }
 
-       dev_info(sdma->dev, "initialized\n");
+               spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus");
+               ret = of_address_to_resource(spba_bus, 0, &spba_res);
+               if (!ret) {
+                       sdma->spba_start_addr = spba_res.start;
+                       sdma->spba_end_addr = spba_res.end;
+               }
+               of_node_put(spba_bus);
+       }
 
        return 0;
 
@@ -1636,7 +1850,6 @@ static int sdma_remove(struct platform_device *pdev)
        }
 
        platform_set_drvdata(pdev, NULL);
-       dev_info(&pdev->dev, "Removed...\n");
        return 0;
 }