/*
 * Topcliff PCH DMA controller driver
 * Copyright (c) 2010 Intel Corporation
 * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pch_dma.h>

#include "dmaengine.h"

#define DRV_NAME "pch-dma"

#define DMA_CTL0_DISABLE		0x0
#define DMA_CTL0_SG			0x1
#define DMA_CTL0_ONESHOT		0x2
#define DMA_CTL0_MODE_MASK_BITS		0x3
#define DMA_CTL0_DIR_SHIFT_BITS		2
#define DMA_CTL0_BITS_PER_CH		4

#define DMA_CTL2_START_SHIFT_BITS	8
#define DMA_CTL2_IRQ_ENABLE_MASK	((1UL << DMA_CTL2_START_SHIFT_BITS) - 1)

#define DMA_STATUS_IDLE			0x0
#define DMA_STATUS_DESC_READ		0x1
#define DMA_STATUS_WAIT			0x2
#define DMA_STATUS_ACCESS		0x3
#define DMA_STATUS_BITS_PER_CH		2
#define DMA_STATUS_MASK_BITS		0x3
#define DMA_STATUS_SHIFT_BITS		16
#define DMA_STATUS_IRQ(x)		(0x1 << (x))
#define DMA_STATUS0_ERR(x)		(0x1 << ((x) + 8))
#define DMA_STATUS2_ERR(x)		(0x1 << (x))

#define DMA_DESC_WIDTH_SHIFT_BITS	12
#define DMA_DESC_WIDTH_1_BYTE		(0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
#define DMA_DESC_WIDTH_2_BYTES		(0x2 << DMA_DESC_WIDTH_SHIFT_BITS)
#define DMA_DESC_WIDTH_4_BYTES		(0x0 << DMA_DESC_WIDTH_SHIFT_BITS)
#define DMA_DESC_MAX_COUNT_1_BYTE	0x3FF
#define DMA_DESC_MAX_COUNT_2_BYTES	0x3FF
#define DMA_DESC_MAX_COUNT_4_BYTES	0x7FF
#define DMA_DESC_END_WITHOUT_IRQ	0x0
#define DMA_DESC_END_WITH_IRQ		0x1
#define DMA_DESC_FOLLOW_WITHOUT_IRQ	0x2
#define DMA_DESC_FOLLOW_WITH_IRQ	0x3

#define MAX_CHAN_NR			12

#define DMA_MASK_CTL0_MODE	0x33333333
#define DMA_MASK_CTL2_MODE	0x00003333

static unsigned int init_nr_desc_per_channel = 64;
module_param(init_nr_desc_per_channel, uint, 0644);
MODULE_PARM_DESC(init_nr_desc_per_channel,
		 "initial descriptors per channel (default: 64)");

struct pch_dma_desc_regs {
	u32	dev_addr;
	u32	mem_addr;
	u32	size;
	u32	next;
};

struct pch_dma_regs {
	u32	dma_ctl0;
	u32	dma_ctl1;
	u32	dma_ctl2;
	u32	dma_ctl3;
	u32	dma_sts0;
	u32	dma_sts1;
	u32	dma_sts2;
	u32	reserved3;
	struct pch_dma_desc_regs desc[MAX_CHAN_NR];
};

struct pch_dma_desc {
	struct pch_dma_desc_regs regs;
	struct dma_async_tx_descriptor txd;
	struct list_head	desc_node;
	struct list_head	tx_list;
};

struct pch_dma_chan {
	struct dma_chan		chan;
	void __iomem *membase;
	enum dma_transfer_direction dir;
	struct tasklet_struct	tasklet;
	unsigned long		err_status;

	spinlock_t		lock;

	struct list_head	active_list;
	struct list_head	queue;
	struct list_head	free_list;
	unsigned int		descs_allocated;
};

#define PDC_DEV_ADDR	0x00
#define PDC_MEM_ADDR	0x04
#define PDC_SIZE	0x08
#define PDC_NEXT	0x0C

#define channel_readl(pdc, name) \
	readl((pdc)->membase + PDC_##name)
#define channel_writel(pdc, name, val) \
	writel((val), (pdc)->membase + PDC_##name)

struct pch_dma {
	struct dma_device	dma;
	void __iomem *membase;
	struct pci_pool		*pool;
	struct pch_dma_regs	regs;
	struct pch_dma_desc_regs ch_regs[MAX_CHAN_NR];
	struct pch_dma_chan	channels[MAX_CHAN_NR];
};

#define PCH_DMA_CTL0	0x00
#define PCH_DMA_CTL1	0x04
#define PCH_DMA_CTL2	0x08
#define PCH_DMA_CTL3	0x0C
#define PCH_DMA_STS0	0x10
#define PCH_DMA_STS1	0x14
#define PCH_DMA_STS2	0x18

#define dma_readl(pd, name) \
	readl((pd)->membase + PCH_DMA_##name)
#define dma_writel(pd, name, val) \
	writel((val), (pd)->membase + PCH_DMA_##name)

static inline
struct pch_dma_desc *to_pd_desc(struct dma_async_tx_descriptor *txd)
{
	return container_of(txd, struct pch_dma_desc, txd);
}

static inline struct pch_dma_chan *to_pd_chan(struct dma_chan *chan)
{
	return container_of(chan, struct pch_dma_chan, chan);
}

static inline struct pch_dma *to_pd(struct dma_device *ddev)
{
	return container_of(ddev, struct pch_dma, dma);
}

static inline struct device *chan2dev(struct dma_chan *chan)
{
	return &chan->dev->device;
}

static inline struct device *chan2parent(struct dma_chan *chan)
{
	return chan->dev->device.parent;
}

static inline
struct pch_dma_desc *pdc_first_active(struct pch_dma_chan *pd_chan)
{
	return list_first_entry(&pd_chan->active_list,
				struct pch_dma_desc, desc_node);
}

static inline
struct pch_dma_desc *pdc_first_queued(struct pch_dma_chan *pd_chan)
{
	return list_first_entry(&pd_chan->queue,
				struct pch_dma_desc, desc_node);
}

static void pdc_enable_irq(struct dma_chan *chan, int enable)
{
	struct pch_dma *pd = to_pd(chan->device);
	u32 val;
	int pos;

	if (chan->chan_id < 8)
		pos = chan->chan_id;
	else
		pos = chan->chan_id + 8;

	val = dma_readl(pd, CTL2);

	if (enable)
		val |= 0x1 << pos;
	else
		val &= ~(0x1 << pos);

	dma_writel(pd, CTL2, val);

	dev_dbg(chan2dev(chan), "pdc_enable_irq: chan %d -> %x\n",
		chan->chan_id, val);
}

static void pdc_set_dir(struct dma_chan *chan)
{
	struct pch_dma_chan *pd_chan = to_pd_chan(chan);
	struct pch_dma *pd = to_pd(chan->device);
	u32 val;
	u32 mask_mode;
	u32 mask_ctl;

	if (chan->chan_id < 8) {
		val = dma_readl(pd, CTL0);

		mask_mode = DMA_CTL0_MODE_MASK_BITS <<
<style>
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
</style><div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># Copyright (c) 2017 Intel Corporation</span>
<span class="c1">#</span>
<span class="c1"># Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span>
<span class="c1"># you may not use this file except in compliance with the License.</span>
<span class="c1"># You may obtain a copy of the License at</span>
<span class="c1">#</span>
<span class="c1">#      http://www.apache.org/licenses/LICENSE-2.0</span>
<span class="c1">#</span>
<span class="c1"># Unless required by applicable law or agreed to in writing, software</span>
<span class="c1"># distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span>
<span class="c1"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span>
<span class="c1"># See the License for the specific language governing permissions and</span>
<span class="c1"># limitations under the License.</span>

<span class="n">DOCUMENTATION</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;</span>
<span class="s1">---</span>
<span class="s1">module: write_string</span>
<span class="s1">short_description: write a string to a file</span>
<span class="s1">description:</span>
<span class="s1">    - write a string to a file without using temp files</span>
<span class="s1">options:</span>
<span class="s1">  path: path to write to</span>
<span class="s1">  val: string to write</span>
<span class="s1">  mode: python file mode (w, wb, a, ab)</span>
<span class="s1">&#39;&#39;&#39;</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">module</span> <span class="o">=</span> <span class="n">AnsibleModule</span><span class="p">(</span>
        <span class="n">argument_spec</span><span class="o">=</span><span class="p">{</span>
            <span class="s1">&#39;path&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;required&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">,</span> <span class="s1">&#39;type&#39;</span><span class="p">:</span> <span class="s1">&#39;path&#39;</span><span class="p">,</span> <span class="s1">&#39;aliases&#39;</span><span class="p">:</span> <span class="p">[</span><span class="s1">&#39;dest&#39;</span><span class="p">]},</span>
            <span class="s1">&#39;val&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;required&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">,</span> <span class="s1">&#39;type&#39;</span><span class="p">:</span> <span class="s1">&#39;str&#39;</span><span class="p">},</span>
            <span class="s1">&#39;mode&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;required&#39;</span><span class="p">:</span> <span class="bp">False</span><span class="p">,</span> <span class="s1">&#39;default&#39;</span><span class="p">:</span> <span class="s2">&quot;w&quot;</span><span class="p">,</span> <span class="s1">&#39;type&#39;</span><span class="p">:</span> <span class="s1">&#39;str&#39;</span><span class="p">,</span>
                     <span class="s1">&#39;choices&#39;</span><span class="p">:</span> <span class="p">[</span><span class="s1">&#39;w&#39;</span><span class="p">,</span> <span class="s1">&#39;wb&#39;</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;ab&#39;</span><span class="p">]}}</span>
    <span class="p">)</span>
    <span class="n">params</span> <span class="o">=</span> <span class="n">module</span><span class="o">.</span><span class="n">params</span>
    <span class="n">path</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">&#39;path&#39;</span><span class="p">]</span>
    <span class="n">mode</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">&#39;mode&#39;</span><span class="p">]</span>
    <span class="n">val</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">&#39;val&#39;</span><span class="p">]</span>
    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span> <span class="k">as</span> <span class="n">file_object</span><span class="p">:</span>
        <span class="n">file_object</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>

    <span class="n">module</span><span class="o">.</span><span class="n">exit_json</span><span class="p">(</span><span class="n">changed</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>


<span class="c1"># &lt;&lt;INCLUDE_ANSIBLE_MODULE_COMMON&gt;&gt;</span>
<span class="kn">from</span> <span class="nn">ansible.module_utils.basic</span> <span class="kn">import</span> <span class="o">*</span>  <span class="c1"># noqa</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</pre></div>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div id="lfcollabprojects-footer">
  <div class="gray-diagonal">
    <div class="footer-inner">
      <p>
        &copy; 2015
        <a href="https://opnfv.org/">Open Platform for NFV Project, Inc</a>., a
        Linux Foundation Collaborative Project. All Rights Reserved.
      </p>
      <p>
        Open Platform for NFV and OPNFV are trademarks of the Open Platform for
        NFV Project, Inc.
      </p>
      <p>
        Linux Foundation is a registered trademark of The Linux Foundation.
        Linux is a registered
        <a
          href="http://www.linuxfoundation.org/programs/legal/trademark"
          title="Linux Mark Institute"
          >trademark</a
        >
        of Linus Torvalds.
      </p>
      <p>
        Please see our
        <a href="https://opnfv.org/about/bylaws-and-policies/terms-use"
          >terms of use</a
        >,
        <a href="https://opnfv.org/about/bylaws-and-policies/trademarks"
          >trademark policy</a
        >, and
        <a href="https://opnfv.org/about/bylaws-and-policies/privacy-policy"
          >privacy policy</a
        >.
      </p>
    </div>
  </div>
</div>
</div> <!-- id=cgit -->
</body>
</html>
   const struct pci_device_id *id)
{
	struct pch_dma *pd;
	struct pch_dma_regs *regs;
	unsigned int nr_channels;
	int err;
	int i;

	nr_channels = id->driver_data;
	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
	if (!pd)
		return -ENOMEM;

	pci_set_drvdata(pdev, pd);

	err = pci_enable_device(pdev);
	if (err) {
		dev_err(&pdev->dev, "Cannot enable PCI device\n");
		goto err_free_mem;
	}

	if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
		dev_err(&pdev->dev, "Cannot find proper base address\n");
		err = -ENODEV;
		goto err_disable_pdev;
	}

	err = pci_request_regions(pdev, DRV_NAME);
	if (err) {
		dev_err(&pdev->dev, "Cannot obtain PCI resources\n");
		goto err_disable_pdev;
	}

	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
	if (err) {
		dev_err(&pdev->dev, "Cannot set proper DMA config\n");
		goto err_free_res;
	}

	regs = pd->membase = pci_iomap(pdev, 1, 0);
	if (!pd->membase) {
		dev_err(&pdev->dev, "Cannot map MMIO registers\n");
		err = -ENOMEM;
		goto err_free_res;
	}

	pci_set_master(pdev);

	err = request_irq(pdev->irq, pd_irq, IRQF_SHARED, DRV_NAME, pd);
	if (err) {
		dev_err(&pdev->dev, "Failed to request IRQ\n");
		goto err_iounmap;
	}

	pd->pool = pci_pool_create("pch_dma_desc_pool", pdev,
				   sizeof(struct pch_dma_desc), 4, 0);
	if (!pd->pool) {
		dev_err(&pdev->dev, "Failed to alloc DMA descriptors\n");
		err = -ENOMEM;
		goto err_free_irq;
	}

	pd->dma.dev = &pdev->dev;

	INIT_LIST_HEAD(&pd->dma.channels);

	for (i = 0; i < nr_channels; i++) {
		struct pch_dma_chan *pd_chan = &pd->channels[i];

		pd_chan->chan.device = &pd->dma;
		dma_cookie_init(&pd_chan->chan);

		pd_chan->membase = &regs->desc[i];

		spin_lock_init(&pd_chan->lock);

		INIT_LIST_HEAD(&pd_chan->active_list);
		INIT_LIST_HEAD(&pd_chan->queue);
		INIT_LIST_HEAD(&pd_chan->free_list);

		tasklet_init(&pd_chan->tasklet, pdc_tasklet,
			     (unsigned long)pd_chan);
		list_add_tail(&pd_chan->chan.device_node, &pd->dma.channels);
	}

	dma_cap_zero(pd->dma.cap_mask);
	dma_cap_set(DMA_PRIVATE, pd->dma.cap_mask);
	dma_cap_set(DMA_SLAVE, pd->dma.cap_mask);

	pd->dma.device_alloc_chan_resources = pd_alloc_chan_resources;
	pd->dma.device_free_chan_resources = pd_free_chan_resources;
	pd->dma.device_tx_status = pd_tx_status;
	pd->dma.device_issue_pending = pd_issue_pending;
	pd->dma.device_prep_slave_sg = pd_prep_slave_sg;
	pd->dma.device_terminate_all = pd_device_terminate_all;

	err = dma_async_device_register(&pd->dma);
	if (err) {
		dev_err(&pdev->dev, "Failed to register DMA device\n");
		goto err_free_pool;
	}

	return 0;

err_free_pool:
	pci_pool_destroy(pd->pool);
err_free_irq:
	free_irq(pdev->irq, pd);
err_iounmap:
	pci_iounmap(pdev, pd->membase);
err_free_res:
	pci_release_regions(pdev);
err_disable_pdev:
	pci_disable_device(pdev);
err_free_mem:
	kfree(pd);
	return err;
}

static void pch_dma_remove(struct pci_dev *pdev)
{
	struct pch_dma *pd = pci_get_drvdata(pdev);
	struct pch_dma_chan *pd_chan;
	struct dma_chan *chan, *_c;

	if (pd) {
		dma_async_device_unregister(&pd->dma);

		free_irq(pdev->irq, pd);

		list_for_each_entry_safe(chan, _c, &pd->dma.channels,
					 device_node) {
			pd_chan = to_pd_chan(chan);

			tasklet_kill(&pd_chan->tasklet);
		}

		pci_pool_destroy(pd->pool);
		pci_iounmap(pdev, pd->membase);
		pci_release_regions(pdev);
		pci_disable_device(pdev);
		kfree(pd);
	}
}

/* PCI Device ID of DMA device */
#define PCI_VENDOR_ID_ROHM             0x10DB
#define PCI_DEVICE_ID_EG20T_PCH_DMA_8CH        0x8810
#define PCI_DEVICE_ID_EG20T_PCH_DMA_4CH        0x8815
#define PCI_DEVICE_ID_ML7213_DMA1_8CH	0x8026
#define PCI_DEVICE_ID_ML7213_DMA2_8CH	0x802B
#define PCI_DEVICE_ID_ML7213_DMA3_4CH	0x8034
#define PCI_DEVICE_ID_ML7213_DMA4_12CH	0x8032
#define PCI_DEVICE_ID_ML7223_DMA1_4CH	0x800B
#define PCI_DEVICE_ID_ML7223_DMA2_4CH	0x800E
#define PCI_DEVICE_ID_ML7223_DMA3_4CH	0x8017
#define PCI_DEVICE_ID_ML7223_DMA4_4CH	0x803B
#define PCI_DEVICE_ID_ML7831_DMA1_8CH	0x8810
#define PCI_DEVICE_ID_ML7831_DMA2_4CH	0x8815

static const struct pci_device_id pch_dma_id_table[] = {
	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_8CH), 8 },
	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_4CH), 4 },
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA1_8CH), 8}, /* UART Video */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA2_8CH), 8}, /* PCMIF SPI */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA3_4CH), 4}, /* FPGA */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA4_12CH), 12}, /* I2S */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA1_4CH), 4}, /* UART */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA2_4CH), 4}, /* Video SPI */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA3_4CH), 4}, /* Security */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA4_4CH), 4}, /* FPGA */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_DMA1_8CH), 8}, /* UART */
	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_DMA2_4CH), 4}, /* SPI */
	{ 0, },
};

static struct pci_driver pch_dma_driver = {
	.name		= DRV_NAME,
	.id_table	= pch_dma_id_table,
	.probe		= pch_dma_probe,
	.remove		= pch_dma_remove,
#ifdef CONFIG_PM
	.suspend	= pch_dma_suspend,
	.resume		= pch_dma_resume,
#endif
};

module_pci_driver(pch_dma_driver);

MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH "
		   "DMA controller driver");
MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, pch_dma_id_table);