diff options
author | Joshua Lock <josh@linux.intel.com> | 2010-05-18 14:51:13 +0100 |
---|---|---|
committer | Joshua Lock <josh@linux.intel.com> | 2010-05-19 12:20:16 +0100 |
commit | 5e8c7c54a9b297dae0081dd19a7bb94e23040a3d (patch) | |
tree | 948e3642c1bf426870b83c72c68c997dce66766c /meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-langwell-dma-driver-3.0.patch | |
parent | 5e07bc91281969d54896dd0a13e3d6134e432027 (diff) | |
download | openembedded-core-contrib-5e8c7c54a9b297dae0081dd19a7bb94e23040a3d.tar.gz |
linux-moblin: add 2.6.33.2 kernel from MeeGo 1.0
Signed-off-by: Joshua Lock <josh@linux.intel.com>
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-langwell-dma-driver-3.0.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-langwell-dma-driver-3.0.patch | 2469 |
1 files changed, 2469 insertions, 0 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-langwell-dma-driver-3.0.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-langwell-dma-driver-3.0.patch new file mode 100644 index 0000000000..d539b60920 --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-langwell-dma-driver-3.0.patch @@ -0,0 +1,2469 @@ +Index: linux-2.6.33/drivers/dma/Kconfig +=================================================================== +--- linux-2.6.33.orig/drivers/dma/Kconfig ++++ linux-2.6.33/drivers/dma/Kconfig +@@ -20,6 +20,37 @@ comment "DMA Devices" + config ASYNC_TX_DISABLE_CHANNEL_SWITCH + bool + ++config INTEL_LNW_DMAC1 ++ bool "Intel MID DMA support for LPE DMA" ++ depends on PCI && X86 && (SND_INTEL_SST||SND_INTEL_LPE) ++ select DMA_ENGINE ++ help ++ Enable support for the Intel(R) MID DMA1 engine present ++ in Intel MID chipsets. ++ ++ Say Y here if you have such a chipset. ++ ++ If unsure, say N. ++ ++config INTEL_LNW_DMAC2 ++ bool "Intel MID DMA support for SC DMA" ++ depends on PCI && X86 ++ select DMA_ENGINE ++ help ++ Enable support for the Intel(R) MID DMA2 engine present ++ in Intel MID chipsets. ++ ++ Say Y here if you have such a chipset. ++ ++ If unsure, say N. ++ ++config LNW_DMA_DEBUG ++ bool "LNW DMA Debugging Enable" ++ depends on INTEL_LNW_DMAC1 || INTEL_LNW_DMAC2 ++ default N ++ help ++ Enable logging in the LNW DMA drivers ++ + config INTEL_IOATDMA + tristate "Intel I/OAT DMA support" + depends on PCI && X86 +Index: linux-2.6.33/drivers/dma/Makefile +=================================================================== +--- linux-2.6.33.orig/drivers/dma/Makefile ++++ linux-2.6.33/drivers/dma/Makefile +@@ -1,5 +1,7 @@ + obj-$(CONFIG_DMA_ENGINE) += dmaengine.o + obj-$(CONFIG_NET_DMA) += iovlock.o ++obj-$(CONFIG_INTEL_LNW_DMAC2) += lnw_dmac2.o ++obj-$(CONFIG_INTEL_LNW_DMAC1) += lnw_dmac1.o + obj-$(CONFIG_DMATEST) += dmatest.o + obj-$(CONFIG_INTEL_IOATDMA) += ioat/ + obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o +Index: linux-2.6.33/drivers/dma/lnw_dma_regs.h +=================================================================== +--- /dev/null ++++ linux-2.6.33/drivers/dma/lnw_dma_regs.h +@@ -0,0 +1,176 @@ ++/* ++ * lnw_dma.c - Intel Langwell DMA Drivers ++ * ++ * Copyright (C) 2008-09 Intel Corp ++ * Author: Vinod Koul <vinod.koul@intel.com> ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * ++ */ ++#ifndef __LNW_DMA_REGS_H__ ++#define __LNW_DMA_REGS_H__ ++ ++#include <linux/dmaengine.h> ++#include <linux/dmapool.h> ++#include <linux/pci_ids.h> ++ ++#define LNW_DMA_DRIVER_VERSION "0.3.1" ++ ++#define DMA_DEBUG ++ ++#define REG_BIT0 0x00000001 ++#define REG_BIT8 0x00000100 ++ ++#define UNMASK_INTR_REG(chan_num) \ ++ ((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num)) ++#define MASK_INTR_REG(chan_num) (REG_BIT8 << chan_num) ++ ++#define ENABLE_CHANNEL(chan_num) \ ++ ((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num)) ++ ++#define DESCS_PER_CHANNEL 16 ++/*DMA Registers*/ ++/*registers associated with channel programming*/ ++#define DMA_REG_SIZE 0x400 ++#define DMA_CH_SIZE 0x58 ++ ++/*CH X REG = (DMA_CH_SIZE)*CH_NO + REG*/ ++#define SAR 0x00 /* Source Address Register*/ ++#define DAR 0x08 /* Destination Address Register*/ ++#define CTL_LOW 0x18 /* Control Register*/ ++#define CTL_HIGH 0x1C /* Control Register*/ ++#define CFG_LOW 0x40 /* Configuration Register Low*/ ++#define CFG_HIGH 0x44 /* Configuration Register high*/ ++ ++#define STATUS_TFR 0x2E8 ++#define STATUS_BLOCK 0x2F0 ++#define STATUS_ERR 0x308 ++ ++#define RAW_TFR 0x2C0 ++#define RAW_BLOCK 0x2C8 ++#define RAW_ERR 0x2E0 ++ ++#define MASK_TFR 0x310 ++#define MASK_BLOCK 0x318 ++#define MASK_SRC_TRAN 0x320 ++#define MASK_DST_TRAN 0x328 ++#define MASK_ERR 0x330 ++ ++#define CLEAR_TFR 0x338 ++#define CLEAR_BLOCK 0x340 ++#define CLEAR_SRC_TRAN 0x348 ++#define CLEAR_DST_TRAN 0x350 ++#define CLEAR_ERR 0x358 ++ ++#define INTR_STATUS 0x360 ++#define DMA_CFG 0x398 ++#define DMA_CHAN_EN 0x3A0 ++ ++/** ++ * struct lnw_dma_chan - internal representation of a DMA channel ++ */ ++struct lnw_dma_chan { ++ struct dma_chan chan; ++ void __iomem *ch_regs; ++ void __iomem *dma_base; ++ int ch_id; ++ spinlock_t lock; ++ dma_cookie_t completed; ++ struct list_head active_list; ++ struct list_head queue; ++ struct list_head free_list; ++ struct lnw_dma_slave *slave; ++ unsigned int descs_allocated; ++ struct lnwdma_device *dma; ++ bool in_use; ++}; ++static inline struct lnw_dma_chan *to_lnw_dma_chan(struct dma_chan *chan) ++{ ++ return container_of(chan, struct lnw_dma_chan, chan); ++} ++ ++/** ++ * struct lnwdma_device - internal representation of a DMA device ++ * @pdev: PCI device ++ * @dma_base: MMIO register space base address of DMA ++ * @lpe_base: MMIO register space base address of LPE ++ * @dma_pool: for allocating DMA descriptors ++ * @common: embedded struct dma_device ++ * @idx: per channel data ++ */ ++struct lnwdma_device { ++ struct pci_dev *pdev; ++ void __iomem *dma_base; ++ struct pci_pool *dma_pool; ++ struct dma_device common; ++ struct tasklet_struct tasklet; ++ struct lnw_dma_chan ch[MAX_CHAN]; ++}; ++ ++static inline struct lnwdma_device *to_lnwdma_device(struct dma_device *common) ++{ ++ return container_of(common, struct lnwdma_device, common); ++} ++ ++struct lnw_dma_desc { ++ void __iomem *block; /*ch ptr*/ ++ struct list_head desc_node; ++ struct dma_async_tx_descriptor txd; ++ size_t len; ++ dma_addr_t sar; ++ dma_addr_t dar; ++ u32 cfg_hi; ++ u32 cfg_lo; ++ u32 ctl_lo; ++ u32 ctl_hi; ++ dma_addr_t next; ++ enum dma_data_direction dirn; ++ enum dma_status status; ++ dma_async_tx_callback callback; ++ void *callback_param; ++ enum lnw_dma_width width; /*width of DMA txn*/ ++ enum lnw_dma_mode cfg_mode; /*mode configuration*/ ++ ++}; ++ ++static inline int test_ch_en(void __iomem *dma, u32 ch_no) ++{ ++ u32 en_reg = ioread32(dma + DMA_CHAN_EN); ++ return (en_reg >> ch_no) & 0x1; ++} ++ ++static inline struct lnw_dma_desc *to_lnw_dma_desc ++ (struct dma_async_tx_descriptor *txd) ++{ ++ return container_of(txd, struct lnw_dma_desc, txd); ++} ++ ++#define _dma_printk(level, format, arg...) \ ++ printk(level "LNW_DMA: %s %d " format, __func__, __LINE__, ## arg) ++ ++#ifdef CONFIG_LNW_DMA_DEBUG ++#define dma_dbg(format, arg...) _dma_printk(KERN_DEBUG, "DBG " format , ## arg) ++#else ++#define dma_dbg(format, arg...) do {} while (0); ++#endif ++ ++#define dma_err(format, arg...) _dma_printk(KERN_ERR, "ERR " format , ## arg) ++#define dma_info(format, arg...) \ ++ _dma_printk(KERN_INFO , "INFO " format , ## arg) ++ ++#endif /*__LNW_DMA_REGS_H__*/ +Index: linux-2.6.33/drivers/dma/lnw_dmac1.c +=================================================================== +--- /dev/null ++++ linux-2.6.33/drivers/dma/lnw_dmac1.c +@@ -0,0 +1,957 @@ ++/* ++ * lnw_dmac1.c - Intel Langwell DMA Drivers ++ * ++ * Copyright (C) 2008-09 Intel Corp ++ * Authhor: Vinod Koul <vinod.koul@intel.com> ++ * The driver design is based on dw_dmac driver ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/interrupt.h> ++#include <sound/intel_lpe.h> ++#include <linux/lnw_dma.h> ++ ++#define MAX_CHAN 2 ++#include "lnw_dma_regs.h" ++ ++MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); ++MODULE_DESCRIPTION("Intel (R) Moorestown Langwell DMAC1 Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION(LNW_DMA_DRIVER_VERSION); ++ ++#define DMA_CH0 6 ++#define DMA_CH1 7 ++#define CH_BLOCK_SIZE 4095 ++ ++static int __devinit lnw_dma1_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id); ++static void __devexit lnw_dma1_remove(struct pci_dev *pdev); ++static void enable_dma1_interrupt(struct lnw_dma_chan *lnwc); ++static void disable_dma1_interrupt(struct lnw_dma_chan *lnwc); ++ ++struct lnw_device { ++ struct pci_dev *pdev; ++ void __iomem *dma_base; ++ struct lnwdma_device *dma; ++}; ++ ++/*CH dep code, if ch no's mapping changes only change here*/ ++static int get_ch_id(int index) ++{ ++ if (index == 0) ++ return DMA_CH0; ++ else if (index == 1) ++ return DMA_CH1; ++ else ++ return -1; ++} ++ ++static int get_ch_index(int ch_id) ++{ ++ if (ch_id == DMA_CH0) ++ return 0; ++ if (ch_id == DMA_CH1) ++ return 1; ++ else ++ return -1; ++} ++ ++static int get_ch_num(int *status) ++{ ++ if (*status & (1 << DMA_CH0)) { ++ *status = *status & (~(1 << DMA_CH0)); ++ return DMA_CH0; ++ } else if (*status & (1 << DMA_CH1)) { ++ *status = *status & (~(1 << DMA_CH1)); ++ return DMA_CH1; ++ } else ++ return -1; ++} ++ ++static int get_block_ts(int len, int tx_width) ++{ ++ int byte_width = 0, block_ts = 0; ++ ++ switch (tx_width) { ++ case LNW_DMA_WIDTH_8BIT: ++ byte_width = 1; ++ break; ++ case LNW_DMA_WIDTH_16BIT: ++ byte_width = 2; ++ break; ++ case LNW_DMA_WIDTH_32BIT: ++ default: ++ byte_width = 4; ++ break; ++ } ++ ++ block_ts = len/byte_width; ++ if (block_ts > CH_BLOCK_SIZE) ++ block_ts = 0xFFFF; ++ return block_ts; ++} ++ ++static struct lnw_dma_desc *lnwc_desc_get1(struct lnw_dma_chan *lnwc) ++{ ++ struct lnw_dma_desc *desc, *_desc; ++ struct lnw_dma_desc *ret = NULL; ++ ++ dma_dbg("called \n"); ++ spin_lock_bh(&lnwc->lock); ++ list_for_each_entry_safe(desc, _desc, &lnwc->free_list, desc_node) { ++ if (async_tx_test_ack(&desc->txd)) { ++ list_del(&desc->desc_node); ++ ret = desc; ++ dma_dbg("got free desc \n"); ++ break; ++ } ++ } ++ spin_unlock_bh(&lnwc->lock); ++ return ret; ++} ++ ++ ++static void lnwc_desc_put1(struct lnw_dma_chan *lnwc, struct lnw_dma_desc *desc) ++{ ++ if (desc) { ++ spin_lock_bh(&lnwc->lock); ++ list_add_tail(&desc->desc_node, &lnwc->free_list); ++ spin_unlock_bh(&lnwc->lock); ++ } ++} ++ ++/* Called with dwc->lock held and bh disabled */ ++static void lnwc_dostart1(struct lnw_dma_chan *lnwc, struct lnw_dma_desc *first) ++{ ++ struct lnwdma_device *lnw = to_lnwdma_device(lnwc->chan.device); ++ ++ dma_dbg("called \n"); ++ /* ASSERT: channel is idle */ ++ if (lnwc->in_use && test_ch_en(lnwc->dma_base, lnwc->ch_id)) { ++ /*error*/ ++ dma_err("channel is busy \n"); ++ /* The tasklet will hopefully advance the queue... */ ++ return; ++ } ++ ++ /*write registers and en*/ ++ iowrite32(first->sar, lnwc->ch_regs + SAR); ++ iowrite32(first->dar, lnwc->ch_regs + DAR); ++ iowrite32(first->cfg_hi, lnwc->ch_regs + CFG_HIGH); ++ iowrite32(first->cfg_lo, lnwc->ch_regs + CFG_LOW); ++ iowrite32(first->ctl_lo, lnwc->ch_regs + CTL_LOW); ++ iowrite32(first->ctl_hi, lnwc->ch_regs + CTL_HIGH); ++ dma_dbg("TX SAR %lx, DAR %lx, CFGL %x, CFGH %x, CTLH %x, CTLL %x \n", ++ first->sar, first->dar, first->cfg_hi, ++ first->cfg_lo, first->ctl_hi, first->ctl_lo); ++ ++ iowrite32(ENABLE_CHANNEL(lnwc->ch_id), lnw->dma_base + DMA_CHAN_EN); ++ first->status = DMA_IN_PROGRESS; ++} ++ ++static void ++lnwc_descriptor_complete1(struct lnw_dma_chan *lnwc, struct lnw_dma_desc *desc) ++{ ++ struct dma_async_tx_descriptor *txd = &desc->txd; ++ dma_async_tx_callback callback = NULL; ++ dma_async_tx_callback callback_txd = NULL; ++ void *param = NULL; ++ void *param_txd = NULL; ++ u32 sar, dar, len; ++ union lnw_dma_ctl_hi ctl_hi; ++ ++ dma_dbg("called \n"); ++ ++ /*check if full tx is complete or not*/ ++ sar = ioread32(lnwc->ch_regs + SAR); ++ dar = ioread32(lnwc->ch_regs + DAR); ++ ++ if (desc->dirn == DMA_FROM_DEVICE) ++ len = dar - desc->dar; ++ else ++ len = sar - desc->sar; ++ ++ dma_dbg("SAR %x DAR %x, DMA done: %x \n", sar, dar, len); ++ if (desc->len > len) { ++ dma_dbg("dirn = %d\n", desc->dirn); ++ dma_dbg("SAR %x DAR %x, len: %x \n", sar, dar, len); ++ /*we have to copy more bytes*/ ++ desc->len -= len; ++ ctl_hi.ctl_hi = desc->ctl_hi; ++ ctl_hi.ctlx.block_ts = get_block_ts(desc->len, desc->width); ++ dma_dbg("setting for %x bytes \n", ctl_hi.ctlx.block_ts); ++ desc->ctl_hi = ctl_hi.ctl_hi; ++ if (desc->cfg_mode == LNW_DMA_MEM_TO_MEM) { ++ sar++; ++ dar++; ++ } else if (desc->dirn == DMA_TO_DEVICE) ++ sar++; ++ else if (desc->dirn == DMA_FROM_DEVICE) ++ dar++; ++ desc->sar = sar; ++ desc->dar = dar; ++ dma_dbg("New SAR %x DAR %x \n", sar, dar); ++ lnwc_dostart1(lnwc, desc); ++ return; ++ } ++ ++ lnwc->completed = txd->cookie; ++ callback = desc->callback; ++ param = desc->callback_param; ++ callback_txd = txd->callback; ++ param_txd = txd->callback_param; ++ ++ list_move(&desc->desc_node, &lnwc->free_list); ++ ++ spin_unlock_bh(&lnwc->lock); ++ dma_dbg("Now we are calling callback \n"); ++ if (callback_txd) { ++ dma_dbg("lnw TXD callback set ... calling \n"); ++ callback_txd(param_txd); ++ spin_lock_bh(&lnwc->lock); ++ return; ++ } ++ if (callback) { ++ dma_dbg("lnw callback set ... calling \n"); ++ callback(param); ++ } ++ spin_lock_bh(&lnwc->lock); ++} ++ ++/*check desc, mark as complete when tx is complete*/ ++static void ++lnwc_scan_descriptors1(struct lnwdma_device *lnw, struct lnw_dma_chan *lnwc) ++{ ++ struct lnw_dma_desc *desc = NULL, *_desc = NULL; ++ u32 status_xfer; ++ ++ dma_dbg("called \n"); ++ status_xfer = ioread32(lnwc->dma_base + RAW_BLOCK); ++ status_xfer = (status_xfer >> lnwc->ch_id) & 0x1; ++ dma_dbg("ch[%d]: status_xfer %x \n", lnwc->ch_id, status_xfer); ++ if (!status_xfer) ++ return; ++ ++ list_for_each_entry_safe(desc, _desc, &lnwc->active_list, desc_node) { ++ if (desc == NULL) ++ continue; ++ if (desc->status == DMA_IN_PROGRESS) { ++ desc->status = DMA_SUCCESS; ++ lnwc_descriptor_complete1(lnwc, desc); ++ } ++ } ++ return; ++} ++ ++/***************************************************************************** ++DMA Functions*/ ++static dma_cookie_t lnw_dma1_tx_submit(struct dma_async_tx_descriptor *tx) ++{ ++ struct lnw_dma_desc *desc = to_lnw_dma_desc(tx); ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(tx->chan); ++ dma_cookie_t cookie; ++ ++ dma_dbg("called \n"); ++ spin_lock_bh(&lnwc->lock); ++ cookie = lnwc->chan.cookie; ++ ++ if (++cookie < 0) ++ cookie = 1; ++ ++ lnwc->chan.cookie = cookie; ++ desc->txd.cookie = cookie; ++ ++ ++ if (list_empty(&lnwc->active_list)) { ++ lnwc_dostart1(lnwc, desc); ++ list_add_tail(&desc->desc_node, &lnwc->active_list); ++ } else { ++ list_add_tail(&desc->desc_node, &lnwc->queue); ++ } ++ spin_unlock_bh(&lnwc->lock); ++ ++ return cookie; ++} ++ ++static void lnw_dma1_issue_pending(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ ++ spin_lock_bh(&lnwc->lock); ++ if (!list_empty(&lnwc->queue)) ++ lnwc_scan_descriptors1(to_lnwdma_device(chan->device), lnwc); ++ spin_unlock_bh(&lnwc->lock); ++} ++ ++static enum dma_status ++lnw_dma1_tx_is_complete(struct dma_chan *chan, ++ dma_cookie_t cookie, ++ dma_cookie_t *done, ++ dma_cookie_t *used) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ dma_cookie_t last_used; ++ dma_cookie_t last_complete; ++ int ret; ++ ++ last_complete = lnwc->completed; ++ last_used = chan->cookie; ++ ++ ret = dma_async_is_complete(cookie, last_complete, last_used); ++ if (ret != DMA_SUCCESS) { ++ lnwc_scan_descriptors1(to_lnwdma_device(chan->device), lnwc); ++ ++ last_complete = lnwc->completed; ++ last_used = chan->cookie; ++ ++ ret = dma_async_is_complete(cookie, last_complete, last_used); ++ } ++ ++ if (done) ++ *done = last_complete; ++ if (used) ++ *used = last_used; ++ ++ return ret; ++} ++ ++static void lnw_dma1_terminate_all(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ struct lnwdma_device *lnw = to_lnwdma_device(chan->device); ++ struct lnw_dma_desc *desc, *_desc; ++ LIST_HEAD(list); ++ ++ /* ASSERT: channel is idle */ ++ if (lnwc->in_use == false) { ++ /*ch is not in use, wrong call*/ ++ return; ++ } ++ spin_lock_bh(&lnwc->lock); ++ list_splice_init(&lnwc->free_list, &list); ++ lnwc->descs_allocated = 0; ++ lnwc->slave = NULL; ++ ++ /* Disable interrupts */ ++ disable_dma1_interrupt(lnwc); ++ ++ spin_unlock_bh(&lnwc->lock); ++ list_for_each_entry_safe(desc, _desc, &list, desc_node) { ++ dma_dbg("freeing descriptor %p\n", desc); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ return; ++} ++ ++static struct dma_async_tx_descriptor * ++lnw_dma1_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_data_direction direction, ++ unsigned long flags) ++{ ++ /*not supported now*/ ++ return NULL; ++} ++ ++static struct dma_async_tx_descriptor * ++lnw_dma1_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, ++ dma_addr_t src, size_t len, unsigned long flags) ++{ ++ struct lnw_dma_chan *lnwc; ++ struct lnw_dma_desc *desc = NULL; ++ struct lnw_dma_slave *lnws; ++ union lnw_dma_ctl_lo ctl_lo; ++ union lnw_dma_ctl_hi ctl_hi; ++ union lnw_dma_cfg_lo cfg_lo; ++ union lnw_dma_cfg_hi cfg_hi; ++ enum lnw_dma_width width = 0; ++ ++ dma_dbg("called \n"); ++ WARN_ON(!chan); ++ if (!len) ++ return NULL; ++ ++ lnws = chan->private; ++ WARN_ON(!lnws); ++ ++ lnwc = to_lnw_dma_chan(chan); ++ WARN_ON(!lnwc); ++ ++ dma_dbg("called for CH %d\n", lnwc->ch_id); ++ dma_dbg("Cfg passed Mode %x, Dirn %x, HS %x, Width %x \n", ++ lnws->cfg_mode, lnws->dirn, lnws->hs_mode, lnws->src_width); ++ ++ /*calculate CFG_LO*/ ++ if (lnws->hs_mode == LNW_DMA_SW_HS) { ++ cfg_lo.cfg_lo = 0; ++ cfg_lo.cfgx.hs_sel_dst = 1; ++ cfg_lo.cfgx.hs_sel_src = 1; ++ } else if (lnws->hs_mode == LNW_DMA_HW_HS) ++ cfg_lo.cfg_lo = 0x00000; ++ ++ /*calculate CFG_HI*/ ++ if (lnws->cfg_mode == LNW_DMA_MEM_TO_MEM) { ++ /*SW HS only*/ ++ dma_dbg("CFG: Mem to mem dma \n"); ++ cfg_hi.cfg_hi = 0; ++ } else { ++ dma_dbg("HW DMA \n"); ++ cfg_hi.cfg_hi = 0; ++ cfg_hi.cfgx.protctl = 0x0; /*default value*/ ++ cfg_hi.cfgx.fifo_mode = 1; ++ if (lnws->dirn == DMA_TO_DEVICE) { ++ cfg_hi.cfgx.src_per = 0; ++ cfg_hi.cfgx.dst_per = 3; ++ } else if (lnws->dirn == DMA_FROM_DEVICE) { ++ cfg_hi.cfgx.src_per = 2; ++ cfg_hi.cfgx.dst_per = 0; ++ } ++ } ++ ++ /*calculate CTL_HI*/ ++ ctl_hi.ctlx.reser = 0; ++ width = lnws->src_width; ++ ++ ctl_hi.ctlx.block_ts = get_block_ts(len, width); ++ ++ /*calculate CTL_LO*/ ++ ctl_lo.ctl_lo = 0; ++ ctl_lo.ctlx.int_en = 1; ++ ctl_lo.ctlx.dst_tr_width = lnws->dst_width; ++ ctl_lo.ctlx.src_tr_width = lnws->src_width; ++ ctl_lo.ctlx.dst_msize = lnws->src_msize; ++ ctl_lo.ctlx.src_msize = lnws->dst_msize; ++ ++ if (lnws->cfg_mode == LNW_DMA_MEM_TO_MEM) { ++ dma_dbg("CTL: Mem to mem dma \n"); ++ ctl_lo.ctlx.tt_fc = 0; ++ ctl_lo.ctlx.sinc = 0; ++ ctl_lo.ctlx.dinc = 0; ++ } else { ++ if (lnws->dirn == DMA_TO_DEVICE) { ++ dma_dbg("CTL: DMA_TO_DEVICE \n"); ++ ctl_lo.ctlx.sinc = 0; ++ ctl_lo.ctlx.dinc = 2; ++ ctl_lo.ctlx.tt_fc = 1; ++ } else if (lnws->dirn == DMA_FROM_DEVICE) { ++ dma_dbg("CTL: DMA_FROM_DEVICE \n"); ++ ctl_lo.ctlx.sinc = 2; ++ ctl_lo.ctlx.dinc = 0; ++ ctl_lo.ctlx.tt_fc = 2; ++ } ++ } ++ ++ dma_dbg("Calc CTL LO %x, CTL HI %x, CFG LO %x, CFG HI %x\n", ++ ctl_lo.ctl_lo, ctl_hi.ctl_hi, cfg_lo.cfg_lo, cfg_hi.cfg_hi); ++ ++ enable_dma1_interrupt(lnwc); ++ ++ desc = lnwc_desc_get1(lnwc); ++ if (desc == NULL) ++ goto err_desc_get; ++ desc->sar = src; ++ desc->dar = dest ; ++ desc->len = len; ++ desc->cfg_hi = cfg_hi.cfg_hi; ++ desc->cfg_lo = cfg_lo.cfg_lo; ++ desc->ctl_lo = ctl_lo.ctl_lo; ++ desc->ctl_hi = ctl_hi.ctl_hi; ++ desc->width = width; ++ desc->dirn = lnws->dirn; ++ if (lnws->callback) { ++ desc->callback = lnws->callback; ++ desc->callback_param = lnws->callback_param; ++ dma_dbg("Callback passed... setting\n"); ++ } else ++ desc->callback = NULL; ++ return &desc->txd; ++ ++err_desc_get: ++ dma_err("Failed to get desc \n"); ++ lnwc_desc_put1(lnwc, desc); ++ return NULL; ++} ++ ++static void lnw_dma1_free_chan_resources(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ struct lnwdma_device *lnw = to_lnwdma_device(chan->device); ++ struct lnw_dma_desc *desc, *_desc; ++ ++ dma_dbg("..called for ch_id %d, lnwch_id %d\n", ++ chan->chan_id, lnwc->ch_id); ++ if (true == lnwc->in_use) { ++ /*trying to free ch in use!!!!!*/ ++ dma_err("trying to free ch in use \n"); ++ } ++ ++ spin_lock_bh(&lnwc->lock); ++ lnwc->descs_allocated = 0; ++ list_for_each_entry_safe(desc, _desc, &lnwc->active_list, desc_node) { ++ dma_dbg("del active \n"); ++ list_del(&desc->desc_node); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ list_for_each_entry_safe(desc, _desc, &lnwc->free_list, desc_node) { ++ list_del(&desc->desc_node); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ list_for_each_entry_safe(desc, _desc, &lnwc->queue, desc_node) { ++ dma_dbg("del queue \n"); ++ list_del(&desc->desc_node); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ spin_unlock_bh(&lnwc->lock); ++ lnwc->in_use = false; ++ chan->client_count--; ++ /* Disable CH interrupts */ ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnw->dma_base + MASK_BLOCK); ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnw->dma_base + MASK_ERR); ++ dma_dbg("done \n"); ++} ++ ++static int lnw_dma1_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ struct lnwdma_device *lnw = to_lnwdma_device(chan->device); ++ struct lnw_dma_desc *desc; ++ dma_addr_t phys; ++ int i = 0; ++ ++ dma_dbg("called \n"); ++ ++ /* ASSERT: channel is idle */ ++ if (test_ch_en(lnw->dma_base, lnwc->ch_id)) { ++ /*ch is not idle*/ ++ dma_err(".ch not idle\n"); ++ return -EIO; ++ } ++ dma_dbg("..called for ch_id %d, lnwch_id %d\n", ++ chan->chan_id, lnwc->ch_id); ++ lnwc->completed = chan->cookie = 1; ++ ++ chan->client_count++; ++ ++ spin_lock_bh(&lnwc->lock); ++ while (lnwc->descs_allocated < DESCS_PER_CHANNEL) { ++ spin_unlock_bh(&lnwc->lock); ++ desc = pci_pool_alloc(lnw->dma_pool, GFP_KERNEL, &phys); ++ if (!desc) { ++ dma_err("desc failed\n"); ++ return -ENOMEM; ++ /*check*/ ++ } ++ dma_async_tx_descriptor_init(&desc->txd, chan); ++ desc->txd.tx_submit = lnw_dma1_tx_submit; ++ desc->txd.flags = DMA_CTRL_ACK; ++ desc->txd.phys = phys; ++ spin_lock_bh(&lnwc->lock); ++ i = ++lnwc->descs_allocated; ++ list_add_tail(&desc->desc_node, &lnwc->free_list); ++ } ++ spin_unlock_bh(&lnwc->lock); ++ lnwc->in_use = false; ++ dma_dbg("Desc alloc done ret: %d desc\n", i); ++ return i; ++} ++ ++static void lnwc_handle_error1(struct lnwdma_device *lnw, ++ struct lnw_dma_chan *lnwc) ++{ ++ lnwc_scan_descriptors1(lnw, lnwc); ++} ++ ++/****************************************************************************** ++* PCI stuff ++*/ ++static struct pci_device_id lnw_dma1_ids[] = { ++ { PCI_VENDOR_ID_INTEL, 0x0814, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, ++ { 0, } ++}; ++ ++MODULE_DEVICE_TABLE(pci, lnw_dma1_ids); ++ ++static struct pci_driver lnw_dma1_pci = { ++ .name = "Intel LNW DMA1", ++ .id_table = lnw_dma1_ids, ++ .probe = lnw_dma1_probe, ++ .remove = __devexit_p(lnw_dma1_remove), ++}; ++ ++static void dma_tasklet1(unsigned long data) ++{ ++ struct lnwdma_device *lnw = NULL; ++ struct lnw_dma_chan *lnwc = NULL; ++ u32 status; ++ int i, ch_no; ++ ++ dma_dbg("called \n"); ++ lnw = (struct lnwdma_device *)data; ++ if (lnw == NULL) { ++ dma_err("Null param \n"); ++ return; ++ } ++ status = ioread32(lnw->dma_base + RAW_BLOCK); ++ dma_dbg("RAW_TFR %x \n", status); ++ status &= 0xC0; ++ while (status) { ++ /*txn interrupt*/ ++ ch_no = get_ch_num(&status); ++ if (ch_no < 0) { ++ dma_err("Ch no is invalid %x, abort!\n", ch_no); ++ return; ++ } ++ dma_dbg("Got Ch %x, new Status %x \n", ch_no, status); ++ i = get_ch_index(ch_no); ++ if (i < 0) { ++ dma_err("Invalid ch index %x\n", i); ++ return; ++ } ++ dma_dbg("Tx complete interrupt %x, Ch No %d Index %d \n", ++ status, ch_no, i); ++ lnwc = &lnw->ch[i]; ++ if (lnwc == NULL) { ++ dma_err("Null param lnwc\n"); ++ return; ++ } ++ dma_dbg("CH %x \n", lnwc->ch_id); ++ spin_lock_bh(&lnwc->lock); ++ lnwc_scan_descriptors1(lnw, lnwc); ++ dma_dbg("Scan of desc... complete, unmasking\n"); ++ iowrite32((1 << lnwc->ch_id), ++ lnw->dma_base + CLEAR_TFR); ++ dma_dbg("Wrote to clear %x\n", (1 << lnwc->ch_id)); ++ iowrite32((1 << lnwc->ch_id), ++ lnw->dma_base + CLEAR_BLOCK); ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), ++ lnw->dma_base + MASK_TFR); ++ spin_unlock_bh(&lnwc->lock); ++ } ++ ++ dma_dbg("Trf interrupt done... \n"); ++ status = ioread32(lnw->dma_base + RAW_ERR); ++ status &= 0xC0; ++ while (status) { ++ /*err interrupt*/ ++ ch_no = get_ch_num(&status); ++ if (ch_no < 0) { ++ dma_err("Ch no is invalid %x, abort!\n", ch_no); ++ return; ++ } ++ dma_dbg("Got Ch %x, new Status %x \n", ch_no, status); ++ i = get_ch_index(ch_no); ++ if (i < 0) { ++ dma_err("Invalid CH lnwc\n"); ++ return; ++ } ++ dma_dbg("Tx error interrupt %x, No %d Index %d \n", ++ status, ch_no, i); ++ lnwc = &lnw->ch[i]; ++ if (lnwc == NULL) { ++ dma_err("Null param lnwc\n"); ++ return; ++ } ++ spin_lock_bh(&lnwc->lock); ++ lnwc_handle_error1(lnw, lnwc); ++ iowrite32((1 << lnwc->ch_id), ++ lnw->dma_base + CLEAR_ERR); ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), ++ lnw->dma_base + MASK_ERR); ++ spin_unlock_bh(&lnwc->lock); ++ } ++ dma_dbg("Exiting takslet... \n"); ++ return; ++} ++ ++static irqreturn_t lnw_dma1_interrupt(int irq, void *data) ++{ ++ struct lnw_device *lnw = data; ++ u32 status; ++ int call_tasklet = 0; ++ ++ /*check interrupt src*/ ++ lpe_periphral_intr_status(LPE_DMA, &status); ++ if (!status) { ++ /*not our interrupt*/ ++ return IRQ_NONE; ++ } ++ ++ /*DMA Interrupt*/ ++ status = ioread32(lnw->dma_base + RAW_TFR); ++ status &= 0xC0; ++ if (status) { ++ iowrite32((status << 8), lnw->dma_base + MASK_TFR); ++ call_tasklet = 1; ++ } ++ status = ioread32(lnw->dma_base + RAW_ERR); ++ status &= 0xC0; ++ if (status) { ++ iowrite32(MASK_INTR_REG(status), lnw->dma_base + MASK_ERR); ++ call_tasklet = 1; ++ } ++ ++ if (call_tasklet) ++ tasklet_schedule(&lnw->dma->tasklet); ++ ++ return IRQ_HANDLED; ++} ++ ++static void enable_dma1_interrupt(struct lnw_dma_chan *lnwc) ++{ ++ dma_dbg("Called for ch_id %d\n", lnwc->ch_id); ++ ++ lpe_unmask_periphral_intr(LPE_DMA); ++ ++ /*en ch interrupts*/ ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_TFR); ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_ERR); ++ return; ++} ++ ++static void disable_dma1_interrupt(struct lnw_dma_chan *lnwc) ++{ ++ /*Check LPE PISR, make sure fwd is disabled*/ ++ lpe_mask_periphral_intr(LPE_DMA); ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_BLOCK); ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_TFR); ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_ERR); ++ dma_dbg(" called \n"); ++ return; ++} ++ ++static int lnw_setup_dma1(struct pci_dev *pdev) ++{ ++ struct lnw_device *device = pci_get_drvdata(pdev); ++ struct lnwdma_device *dma = NULL; ++ int err, i; ++ ++ dma_dbg("setup_dma called \n"); ++ dma = kzalloc(sizeof(*dma), GFP_KERNEL); ++ if (NULL == dma) { ++ dma_err("kzalloc failed \n"); ++ err = -ENOMEM; ++ goto err_kzalloc; ++ } ++ device->dma = dma; ++ dma->pdev = pdev; ++ dma->dma_base = device->dma_base; ++ ++ /* DMA coherent memory pool for DMA descriptor allocations */ ++ dma->dma_pool = pci_pool_create("dma_desc_pool", pdev, ++ sizeof(struct lnw_dma_desc), ++ 32, 0); ++ if (NULL == dma->dma_pool) { ++ dma_err("pci_pool_create failed \n"); ++ err = -ENOMEM; ++ kfree(dma); ++ goto err_dma_pool; ++ } ++ ++ INIT_LIST_HEAD(&dma->common.channels); ++ ++ ++ /*init CH structures*/ ++ for (i = 0; i < MAX_CHAN; i++) { ++ struct lnw_dma_chan *lnwch = &dma->ch[i]; ++ ++ lnwch->chan.device = &dma->common; ++ lnwch->chan.cookie = 1; ++ lnwch->chan.chan_id = i; ++ lnwch->ch_id = get_ch_id(i); ++ dma_dbg("Init CH %d, ID %d \n", i, lnwch->ch_id); ++ ++ lnwch->dma_base = dma->dma_base; ++ lnwch->ch_regs = dma->dma_base + DMA_CH_SIZE * lnwch->ch_id; ++ lnwch->dma = dma; ++ spin_lock_init(&lnwch->lock); ++ ++ INIT_LIST_HEAD(&lnwch->active_list); ++ INIT_LIST_HEAD(&lnwch->queue); ++ INIT_LIST_HEAD(&lnwch->free_list); ++ /*mask interrupts*/ ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_BLOCK); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_SRC_TRAN); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_DST_TRAN); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_ERR); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_TFR); ++ ++ disable_dma1_interrupt(lnwch); ++ list_add_tail(&lnwch->chan.device_node, &dma->common.channels); ++ } ++ ++ /*init dma structure*/ ++ dma_cap_zero(dma->common.cap_mask); ++ dma_cap_set(DMA_MEMCPY, dma->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, dma->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, dma->common.cap_mask); ++ dma->common.dev = &pdev->dev; ++ dma->common.chancnt = MAX_CHAN; ++ ++ dma->common.device_alloc_chan_resources = ++ lnw_dma1_alloc_chan_resources; ++ dma->common.device_free_chan_resources = ++ lnw_dma1_free_chan_resources; ++ ++ dma->common.device_is_tx_complete = lnw_dma1_tx_is_complete; ++ dma->common.device_prep_dma_memcpy = lnw_dma1_prep_memcpy; ++ dma->common.device_issue_pending = lnw_dma1_issue_pending; ++ dma->common.device_prep_slave_sg = lnw_dma1_prep_slave_sg; ++ dma->common.device_terminate_all = lnw_dma1_terminate_all; ++ ++ /*enable dma cntrl*/ ++ iowrite32(REG_BIT0, dma->dma_base + DMA_CFG); ++ ++ /*register irq*/ ++ err = request_irq(pdev->irq, lnw_dma1_interrupt, ++ IRQF_SHARED, lnw_dma1_pci.name, device); ++ if (0 != err) ++ goto err_irq; ++ ++ /*register device w/ engine*/ ++ err = dma_async_device_register(&dma->common); ++ if (0 != err) { ++ dma_err("device_register failed: %d \n", err); ++ goto err_engine; ++ } ++ tasklet_init(&dma->tasklet, dma_tasklet1, (unsigned long)dma); ++ dma_dbg("...done \n"); ++ return 0; ++ ++err_engine: ++ free_irq(pdev->irq, device); ++err_irq: ++ pci_pool_destroy(dma->dma_pool); ++ kfree(dma); ++err_dma_pool: ++err_kzalloc: ++ dma_err("setup_dma failed: %d \n", err); ++ return err; ++ ++} ++ ++static void lnwdma_shutdown1(struct pci_dev *pdev) ++{ ++ struct lnw_device *device = pci_get_drvdata(pdev); ++ ++ dma_dbg("shutdown called \n"); ++ dma_async_device_unregister(&device->dma->common); ++ pci_pool_destroy(device->dma->dma_pool); ++ if (device->dma_base) ++ iounmap(device->dma_base); ++ free_irq(pdev->irq, device); ++ return; ++} ++ ++static int __devinit ++lnw_dma1_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct lnw_device *device = NULL; ++ u32 base_addr = 0, bar_size = 0; ++ int err = 0; ++ ++ dma_info("probe called for %x \n", pdev->device); ++ err = pci_enable_device(pdev); ++ if (err) ++ goto err_enable_device; ++ ++ err = pci_request_regions(pdev, lnw_dma1_pci.name); ++ if (err) ++ goto err_request_regions; ++ ++ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (err) ++ goto err_set_dma_mask; ++ ++ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (err) ++ goto err_set_dma_mask; ++ ++ device = kzalloc(sizeof(*device), GFP_KERNEL); ++ if (!device) { ++ dma_err("kzalloc failed \n"); ++ err = -ENOMEM; ++ goto err_kzalloc; ++ } ++ device->pdev = pci_dev_get(pdev); ++ ++ base_addr = pci_resource_start(pdev, 0); ++ bar_size = pci_resource_len(pdev, 0); ++ dma_dbg("BAR0 %x Size %x \n", base_addr, bar_size); ++ device->dma_base = ioremap_nocache(base_addr, DMA_REG_SIZE); ++ if (!device->dma_base) { ++ dma_err("ioremap failed \n"); ++ err = -ENOMEM; ++ goto err_ioremap1; ++ } ++ pci_set_drvdata(pdev, device); ++ pci_set_master(pdev); ++ ++ err = lnw_setup_dma1(pdev); ++ if (err) ++ goto err_dma; ++ ++ return 0; ++ ++err_dma: ++ iounmap(device->dma_base); ++err_ioremap1: ++ pci_dev_put(pdev); ++ kfree(device); ++err_kzalloc: ++err_set_dma_mask: ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++err_request_regions: ++err_enable_device: ++ dma_err("Probe failed %d\n", err); ++ return err; ++} ++ ++static void __devexit lnw_dma1_remove(struct pci_dev *pdev) ++{ ++ struct lnw_device *device = pci_get_drvdata(pdev); ++ ++ lnwdma_shutdown1(pdev); ++ pci_dev_put(pdev); ++ kfree(device); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++} ++ ++static int __init lnw_dma1_init(void) ++{ ++ dma_info("LNW DMA Driver\n Version %s \n", LNW_DMA_DRIVER_VERSION); ++ return pci_register_driver(&lnw_dma1_pci); ++} ++late_initcall(lnw_dma1_init); ++ ++static void __exit lnw_dma1_exit(void) ++{ ++ pci_unregister_driver(&lnw_dma1_pci); ++} ++module_exit(lnw_dma1_exit); ++ +Index: linux-2.6.33/drivers/dma/lnw_dmac2.c +=================================================================== +--- /dev/null ++++ linux-2.6.33/drivers/dma/lnw_dmac2.c +@@ -0,0 +1,947 @@ ++/* ++ * lnw_dmac2.c - Intel Langwell DMA Drivers ++ * ++ * Copyright (C) 2008-09 Intel Corp ++ * Author: Vinod Koul <vinod.koul@intel.com> ++ * The driver design is based on dw_dmac driver ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/interrupt.h> ++#include <linux/lnw_dma.h> ++ ++#define MAX_CHAN 2 ++#include "lnw_dma_regs.h" ++ ++MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); ++MODULE_DESCRIPTION("Intel (R) Moorestown Langwell DMAC2 Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION(LNW_DMA_DRIVER_VERSION); ++ ++#define DMA_CH0 0 ++#define DMA_CH1 1 ++#define CH_BLOCK_SIZE 2047 ++ ++static int __devinit lnw_dma2_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id); ++static void __devexit lnw_dma2_remove(struct pci_dev *pdev); ++static void enable_dma2_interrupt(struct lnw_dma_chan *lnwc); ++ ++struct lnw_device { ++ struct pci_dev *pdev; ++ void __iomem *dma_base; ++ struct lnwdma_device *dma; ++}; ++ ++/*CH dep code, if ch no's mapping changes only change here*/ ++static int get_ch_id(int index) ++{ ++ if (index == 0) ++ return DMA_CH0; ++ else if (index == 1) ++ return DMA_CH1; ++ else ++ return -1; ++} ++ ++static int get_ch_index(int ch_id) ++{ ++ if (ch_id == DMA_CH0) ++ return 0; ++ if (ch_id == DMA_CH1) ++ return 1; ++ else ++ return -1; ++} ++ ++static int get_ch_num(int *status) ++{ ++ if (*status & (1 << DMA_CH0)) { ++ *status = *status & (~(1 << DMA_CH0)); ++ return DMA_CH0; ++ } else if (*status & (1 << DMA_CH1)) { ++ *status = *status & (~(1 << DMA_CH1)); ++ return DMA_CH1; ++ } else ++ return -1; ++} ++ ++static int get_block_ts(int len, int tx_width) ++{ ++ int byte_width = 0, block_ts = 0; ++ ++ switch (tx_width) { ++ case LNW_DMA_WIDTH_8BIT: ++ byte_width = 1; ++ break; ++ case LNW_DMA_WIDTH_16BIT: ++ byte_width = 2; ++ break; ++ case LNW_DMA_WIDTH_32BIT: ++ default: ++ byte_width = 4; ++ break; ++ } ++ ++ block_ts = len/byte_width; ++ if (block_ts > CH_BLOCK_SIZE) ++ block_ts = 0xFFFF; ++ return block_ts; ++} ++ ++static struct lnw_dma_desc *lnwc_desc_get(struct lnw_dma_chan *lnwc) ++{ ++ struct lnw_dma_desc *desc, *_desc; ++ struct lnw_dma_desc *ret = NULL; ++ ++ dma_dbg("called \n"); ++ spin_lock_bh(&lnwc->lock); ++ list_for_each_entry_safe(desc, _desc, &lnwc->free_list, desc_node) { ++ if (async_tx_test_ack(&desc->txd)) { ++ list_del(&desc->desc_node); ++ ret = desc; ++ dma_dbg("got free desc \n"); ++ break; ++ } ++ } ++ spin_unlock_bh(&lnwc->lock); ++ return ret; ++} ++ ++static void lnwc_desc_put(struct lnw_dma_chan *lnwc, struct lnw_dma_desc *desc) ++{ ++ if (desc) { ++ spin_lock_bh(&lnwc->lock); ++ list_add_tail(&desc->desc_node, &lnwc->free_list); ++ spin_unlock_bh(&lnwc->lock); ++ } ++} ++ ++/* Called with lock held and bh disabled */ ++static void lnwc_dostart(struct lnw_dma_chan *lnwc, struct lnw_dma_desc *first) ++{ ++ struct lnwdma_device *lnw = to_lnwdma_device(lnwc->chan.device); ++ ++ dma_dbg("called \n"); ++ /* channel is idle */ ++ if (lnwc->in_use && test_ch_en(lnwc->dma_base, lnwc->ch_id)) { ++ /*error*/ ++ dma_err("channel is busy \n"); ++ /* The tasklet will hopefully advance the queue... */ ++ return; ++ } ++ ++ /*write registers and en*/ ++ iowrite32(first->sar, lnwc->ch_regs + SAR); ++ iowrite32(first->dar, lnwc->ch_regs + DAR); ++ iowrite32(first->cfg_hi, lnwc->ch_regs + CFG_HIGH); ++ iowrite32(first->cfg_lo, lnwc->ch_regs + CFG_LOW); ++ iowrite32(first->ctl_lo, lnwc->ch_regs + CTL_LOW); ++ iowrite32(first->ctl_hi, lnwc->ch_regs + CTL_HIGH); ++ dma_dbg("TX SAR %lx, DAR %lx, CFGL %x, CFGH %x, CTLH %x, CTLL %x \n", ++ first->sar, first->dar, first->cfg_hi, ++ first->cfg_lo, first->ctl_hi, first->ctl_lo); ++ ++ iowrite32(ENABLE_CHANNEL(lnwc->ch_id), lnw->dma_base + DMA_CHAN_EN); ++ first->status = DMA_IN_PROGRESS; ++} ++ ++static void ++lnwc_descriptor_complete(struct lnw_dma_chan *lnwc, struct lnw_dma_desc *desc) ++{ ++ struct dma_async_tx_descriptor *txd = &desc->txd; ++ dma_async_tx_callback callback = NULL; ++ dma_async_tx_callback callback_txd = NULL; ++ void *param = NULL; ++ void *param_txd = NULL; ++ u32 sar, dar, len; ++ union lnw_dma_ctl_hi ctl_hi; ++ ++ dma_dbg("called \n"); ++ ++ /*check if full tx is complete or not*/ ++ sar = ioread32(lnwc->ch_regs + SAR); ++ dar = ioread32(lnwc->ch_regs + DAR); ++ ++ if (desc->dirn == DMA_FROM_DEVICE) ++ len = dar - desc->dar; ++ else ++ len = sar - desc->sar; ++ ++ dma_dbg("SAR %x DAR %x, DMA done: %x \n", sar, dar, len); ++ if (desc->len > len) { ++ dma_dbg("dirn = %d\n", desc->dirn); ++ dma_dbg("SAR %x DAR %x, len: %x \n", sar, dar, len); ++ /*we have to copy more bytes*/ ++ desc->len -= len; ++ ctl_hi.ctl_hi = desc->ctl_hi; ++ ctl_hi.ctlx.block_ts = get_block_ts(desc->len, desc->width); ++ dma_dbg("setting for %x bytes \n", ctl_hi.ctlx.block_ts); ++ desc->ctl_hi = ctl_hi.ctl_hi; ++ if (desc->cfg_mode == LNW_DMA_MEM_TO_MEM) { ++ sar++; ++ dar++; ++ } else if (desc->dirn == DMA_TO_DEVICE) ++ sar++; ++ else if (desc->dirn == DMA_FROM_DEVICE) ++ dar++; ++ desc->sar = sar; ++ desc->dar = dar; ++ dma_dbg("New SAR %x DAR %x \n", sar, dar); ++ lnwc_dostart(lnwc, desc); ++ return; ++ } ++ ++ lnwc->completed = txd->cookie; ++ callback = desc->callback; ++ param = desc->callback_param; ++ callback_txd = txd->callback; ++ param_txd = txd->callback_param; ++ ++ list_move(&desc->desc_node, &lnwc->free_list); ++ ++ spin_unlock_bh(&lnwc->lock); ++ dma_dbg("Now we are calling callback \n"); ++ if (callback_txd) { ++ dma_dbg("lnw TXD callback set ... calling \n"); ++ callback_txd(param_txd); ++ spin_lock_bh(&lnwc->lock); ++ return; ++ } ++ if (callback) { ++ dma_dbg("lnw callback set ... calling \n"); ++ callback(param); ++ } ++ spin_lock_bh(&lnwc->lock); ++ ++} ++ ++/*check desc, mark as complete when tx is complete*/ ++static void ++lnwc_scan_descriptors(struct lnwdma_device *lnw, struct lnw_dma_chan *lnwc) ++{ ++ struct lnw_dma_desc *desc = NULL, *_desc = NULL; ++ u32 status_xfer; ++ ++ dma_dbg("called \n"); ++ status_xfer = ioread32(lnwc->dma_base + RAW_TFR); ++ status_xfer = (status_xfer >> lnwc->ch_id) & 0x1; ++ dma_dbg("ch[%d]: status_xfer %x \n", lnwc->ch_id, status_xfer); ++ if (!status_xfer) ++ return; ++ ++ /*tx is complete*/ ++ list_for_each_entry_safe(desc, _desc, &lnwc->active_list, desc_node) { ++ if (desc == NULL) ++ continue; ++ if (desc->status == DMA_IN_PROGRESS) { ++ desc->status = DMA_SUCCESS; ++ lnwc_descriptor_complete(lnwc, desc); ++ } ++ } ++ return; ++} ++ ++/***************************************************************************** ++DMA Functions*/ ++static dma_cookie_t lnw_dma2_tx_submit(struct dma_async_tx_descriptor *tx) ++{ ++ struct lnw_dma_desc *desc = to_lnw_dma_desc(tx); ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(tx->chan); ++ dma_cookie_t cookie; ++ ++ dma_dbg("called \n"); ++ ++ spin_lock_bh(&lnwc->lock); ++ cookie = lnwc->chan.cookie; ++ ++ if (++cookie < 0) ++ cookie = 1; ++ ++ lnwc->chan.cookie = cookie; ++ desc->txd.cookie = cookie; ++ ++ if (list_empty(&lnwc->active_list)) { ++ lnwc_dostart(lnwc, desc); ++ list_add_tail(&desc->desc_node, &lnwc->active_list); ++ } else { ++ list_add_tail(&desc->desc_node, &lnwc->queue); ++ } ++ spin_unlock_bh(&lnwc->lock); ++ ++ return cookie; ++} ++ ++static void lnw_dma2_issue_pending(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ ++ spin_lock_bh(&lnwc->lock); ++ if (!list_empty(&lnwc->queue)) ++ lnwc_scan_descriptors(to_lnwdma_device(chan->device), lnwc); ++ spin_unlock_bh(&lnwc->lock); ++} ++ ++static enum dma_status ++lnw_dma2_tx_is_complete(struct dma_chan *chan, ++ dma_cookie_t cookie, ++ dma_cookie_t *done, ++ dma_cookie_t *used) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ dma_cookie_t last_used; ++ dma_cookie_t last_complete; ++ int ret; ++ ++ last_complete = lnwc->completed; ++ last_used = chan->cookie; ++ ++ ret = dma_async_is_complete(cookie, last_complete, last_used); ++ if (ret != DMA_SUCCESS) { ++ lnwc_scan_descriptors(to_lnwdma_device(chan->device), lnwc); ++ ++ last_complete = lnwc->completed; ++ last_used = chan->cookie; ++ ++ ret = dma_async_is_complete(cookie, last_complete, last_used); ++ } ++ ++ if (done) ++ *done = last_complete; ++ if (used) ++ *used = last_used; ++ ++ return ret; ++} ++ ++static void lnw_dma2_terminate_all(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ struct lnwdma_device *lnw = to_lnwdma_device(chan->device); ++ struct lnw_dma_desc *desc, *_desc; ++ LIST_HEAD(list); ++ ++ /* ASSERT: channel is idle */ ++ if (lnwc->in_use == false) { ++ /*ch is not in use, wrong call*/ ++ return; ++ } ++ spin_lock_bh(&lnwc->lock); ++ list_splice_init(&lnwc->free_list, &list); ++ lnwc->descs_allocated = 0; ++ lnwc->slave = NULL; ++ ++ /* Disable interrupts*/ ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnw->dma_base + MASK_BLOCK); ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnw->dma_base + MASK_ERR); ++ ++ spin_unlock_bh(&lnwc->lock); ++ list_for_each_entry_safe(desc, _desc, &list, desc_node) { ++ dma_dbg("freeing descriptor %p\n", desc); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ ++ return; ++} ++ ++static struct dma_async_tx_descriptor * ++lnw_dma2_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_data_direction direction, ++ unsigned long flags) ++{ ++ /*not supported now*/ ++ return NULL; ++} ++ ++static struct dma_async_tx_descriptor * ++lnw_dma2_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, ++ dma_addr_t src, size_t len, unsigned long flags) ++{ ++ struct lnw_dma_chan *lnwc; ++ struct lnw_dma_desc *desc = NULL; ++ struct lnw_dma_slave *lnws; ++ union lnw_dma_ctl_lo ctl_lo; ++ union lnw_dma_ctl_hi ctl_hi; ++ union lnw_dma_cfg_lo cfg_lo; ++ union lnw_dma_cfg_hi cfg_hi; ++ enum lnw_dma_width width = 0; ++ ++ dma_dbg("called \n"); ++ WARN_ON(!chan); ++ if (!len) ++ return NULL; ++ ++ lnws = chan->private; ++ WARN_ON(!lnws); ++ ++ lnwc = to_lnw_dma_chan(chan); ++ WARN_ON(!lnwc); ++ ++ dma_dbg("called for CH %d\n", lnwc->ch_id); ++ dma_dbg("Cfg passed Mode %x, Dirn %x, HS %x, Width %x \n", ++ lnws->cfg_mode, lnws->dirn, lnws->hs_mode, lnws->src_width); ++ ++ /*calculate CFG_LO*/ ++ if (lnws->hs_mode == LNW_DMA_SW_HS) { ++ cfg_lo.cfg_lo = 0; ++ cfg_lo.cfgx.hs_sel_dst = 1; ++ cfg_lo.cfgx.hs_sel_src = 1; ++ } else if (lnws->hs_mode == LNW_DMA_HW_HS) ++ cfg_lo.cfg_lo = 0x00000; ++ ++ /*calculate CFG_HI*/ ++ if (lnws->cfg_mode == LNW_DMA_MEM_TO_MEM) { ++ /*SW HS only*/ ++ dma_dbg("CFG: Mem to mem dma \n"); ++ cfg_hi.cfg_hi = 0; ++ } else { ++ dma_dbg("HW DMA \n"); ++ cfg_hi.cfg_hi = 0; ++ cfg_hi.cfgx.protctl = 0x1; /*default value*/ ++ cfg_hi.cfgx.src_per = get_ch_index(lnwc->ch_id); ++ cfg_hi.cfgx.dst_per = get_ch_index(lnwc->ch_id); ++ } ++ ++ /*calculate CTL_HI*/ ++ ctl_hi.ctlx.reser = 0; ++ width = lnws->src_width; ++ ctl_hi.ctlx.block_ts = get_block_ts(len, width); ++ ++ /*calculate CTL_LO*/ ++ ctl_lo.ctl_lo = 0; ++ ctl_lo.ctlx.int_en = 1; ++ ctl_lo.ctlx.dst_tr_width = lnws->dst_width; ++ ctl_lo.ctlx.src_tr_width = lnws->src_width; ++ ctl_lo.ctlx.dst_msize = lnws->src_msize; ++ ctl_lo.ctlx.src_msize = lnws->dst_msize; ++ ++ if (lnws->cfg_mode == LNW_DMA_MEM_TO_MEM) { ++ dma_dbg("CTL: Mem to mem dma \n"); ++ ctl_lo.ctlx.tt_fc = 0; ++ ctl_lo.ctlx.sinc = 0; ++ ctl_lo.ctlx.dinc = 0; ++ } else { ++ if (lnws->dirn == DMA_TO_DEVICE) { ++ dma_dbg("CTL: DMA_TO_DEVICE \n"); ++ ctl_lo.ctlx.sinc = 0; ++ ctl_lo.ctlx.dinc = 2; ++ ctl_lo.ctlx.tt_fc = 1; ++ } else if (lnws->dirn == DMA_FROM_DEVICE) { ++ dma_dbg("CTL: DMA_FROM_DEVICE \n"); ++ ctl_lo.ctlx.sinc = 2; ++ ctl_lo.ctlx.dinc = 0; ++ ctl_lo.ctlx.tt_fc = 2; ++ } ++ } ++ ++ dma_dbg("Calc CTL LO %x, CTL HI %x, CFG LO %x, CFG HI %x\n", ++ ctl_lo.ctl_lo, ctl_hi.ctl_hi, cfg_lo.cfg_lo, cfg_hi.cfg_hi); ++ ++ enable_dma2_interrupt(lnwc); ++ ++ desc = lnwc_desc_get(lnwc); ++ if (desc == NULL) ++ goto err_desc_get; ++ desc->sar = src; ++ desc->dar = dest ; ++ desc->len = len; ++ desc->cfg_hi = cfg_hi.cfg_hi; ++ desc->cfg_lo = cfg_lo.cfg_lo; ++ desc->ctl_lo = ctl_lo.ctl_lo; ++ desc->ctl_hi = ctl_hi.ctl_hi; ++ desc->width = width; ++ desc->dirn = lnws->dirn; ++ if (lnws->callback) { ++ desc->callback = lnws->callback; ++ desc->callback_param = lnws->callback_param; ++ dma_dbg("Callback passed... setting\n"); ++ } else ++ desc->callback = NULL; ++ return &desc->txd; ++ ++err_desc_get: ++ dma_err("Failed to get desc \n"); ++ lnwc_desc_put(lnwc, desc); ++ return NULL; ++} ++ ++ ++static void lnw_dma2_free_chan_resources(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ struct lnwdma_device *lnw = to_lnwdma_device(chan->device); ++ struct lnw_dma_desc *desc, *_desc; ++ ++ dma_dbg("..called for ch_id %d, lnwch_id %d\n", ++ chan->chan_id, lnwc->ch_id); ++ if (true == lnwc->in_use) { ++ /*trying to free ch in use!!!!!*/ ++ dma_err("trying to free ch in use \n"); ++ } ++ ++ spin_lock_bh(&lnwc->lock); ++ lnwc->descs_allocated = 0; ++ list_for_each_entry_safe(desc, _desc, &lnwc->active_list, desc_node) { ++ dma_dbg("del active \n"); ++ list_del(&desc->desc_node); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ list_for_each_entry_safe(desc, _desc, &lnwc->free_list, desc_node) { ++ list_del(&desc->desc_node); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ list_for_each_entry_safe(desc, _desc, &lnwc->queue, desc_node) { ++ dma_dbg("del queue \n"); ++ list_del(&desc->desc_node); ++ pci_pool_free(lnw->dma_pool, desc, desc->txd.phys); ++ } ++ spin_unlock_bh(&lnwc->lock); ++ lnwc->in_use = false; ++ chan->client_count--; ++ /* Disable CH interrupts*/ ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnw->dma_base + MASK_BLOCK); ++ iowrite32(MASK_INTR_REG(lnwc->ch_id), lnw->dma_base + MASK_ERR); ++ dma_dbg("done \n"); ++} ++ ++static int lnw_dma2_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct lnw_dma_chan *lnwc = to_lnw_dma_chan(chan); ++ struct lnwdma_device *lnw = to_lnwdma_device(chan->device); ++ struct lnw_dma_desc *desc; ++ dma_addr_t phys; ++ int i = 0; ++ ++ dma_dbg("called \n"); ++ ++ /* ASSERT: channel is idle */ ++ if (test_ch_en(lnw->dma_base, lnwc->ch_id)) { ++ /*ch is not idle*/ ++ dma_err(".ch not idle\n"); ++ return -EIO; ++ } ++ dma_dbg("..called for ch_id %d, lnwch_id %d\n", ++ chan->chan_id, lnwc->ch_id); ++ lnwc->completed = chan->cookie = 1; ++ ++ chan->client_count++; ++ ++ spin_lock_bh(&lnwc->lock); ++ while (lnwc->descs_allocated < DESCS_PER_CHANNEL) { ++ spin_unlock_bh(&lnwc->lock); ++ desc = pci_pool_alloc(lnw->dma_pool, GFP_KERNEL, &phys); ++ if (!desc) { ++ dma_err("desc failed\n"); ++ return -ENOMEM; ++ /*check*/ ++ } ++ dma_async_tx_descriptor_init(&desc->txd, chan); ++ desc->txd.tx_submit = lnw_dma2_tx_submit; ++ desc->txd.flags = DMA_CTRL_ACK; ++ desc->txd.phys = phys; ++ spin_lock_bh(&lnwc->lock); ++ i = ++lnwc->descs_allocated; ++ list_add_tail(&desc->desc_node, &lnwc->free_list); ++ } ++ spin_unlock_bh(&lnwc->lock); ++ lnwc->in_use = false; ++ dma_dbg("Desc alloc done ret: %d desc\n", i); ++ return i; ++} ++ ++static void lnwc_handle_error(struct lnwdma_device *lnw, ++ struct lnw_dma_chan *lnwc) ++{ ++ lnwc_scan_descriptors(lnw, lnwc); ++} ++ ++/****************************************************************************** ++* PCI stuff ++*/ ++static struct pci_device_id lnw_dma2_ids[] = { ++ { PCI_VENDOR_ID_INTEL, 0x0813, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, ++ { 0, } ++}; ++ ++MODULE_DEVICE_TABLE(pci, lnw_dma2_ids); ++ ++static struct pci_driver lnw_dma2_pci = { ++ .name = "Intel LNW DMA2", ++ .id_table = lnw_dma2_ids, ++ .probe = lnw_dma2_probe, ++ .remove = __devexit_p(lnw_dma2_remove), ++}; ++ ++static void dma_tasklet(unsigned long data) ++{ ++ struct lnwdma_device *lnw = NULL; ++ struct lnw_dma_chan *lnwc = NULL; ++ u32 status; ++ int i, ch_no; ++ ++ dma_dbg("called \n"); ++ lnw = (struct lnwdma_device *)data; ++ if (lnw == NULL) { ++ dma_err("Null param \n"); ++ return; ++ } ++ ++ status = ioread32(lnw->dma_base + RAW_TFR); ++ dma_dbg("RAW_TFR %x \n", status); ++ while (status) { ++ /*txn interrupt*/ ++ ch_no = get_ch_num(&status); ++ if (ch_no < 0) { ++ dma_err("Ch no is invalid %x, abort!\n", ch_no); ++ return; ++ } ++ dma_dbg("Got Ch %x, new Status %x \n", ch_no, status); ++ i = get_ch_index(ch_no); ++ if (i < 0) { ++ dma_err("Invalid ch index %x\n", i); ++ return; ++ } ++ dma_dbg("Tx complete interrupt %x, Ch No %d Index %d \n", ++ status, ch_no, i); ++ lnwc = &lnw->ch[i]; ++ if (lnwc == NULL) { ++ dma_err("Null param lnwc\n"); ++ return; ++ } ++ dma_dbg("CH %x \n", lnwc->ch_id); ++ spin_lock_bh(&lnwc->lock); ++ lnwc_scan_descriptors(lnw, lnwc); ++ dma_dbg("Scan of desc... complete, unmasking\n"); ++ iowrite32((1 << lnwc->ch_id), ++ lnw->dma_base + CLEAR_TFR); ++ dma_dbg("Wrote to clear %x\n", (1 << lnwc->ch_id)); ++ iowrite32((1 << lnwc->ch_id), ++ lnw->dma_base + CLEAR_BLOCK); ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), ++ lnw->dma_base + MASK_TFR); ++ spin_unlock_bh(&lnwc->lock); ++ } ++ ++ dma_dbg("Trf interrupt done... \n"); ++ status = ioread32(lnw->dma_base + RAW_ERR); ++ while (status) { ++ /*err interrupt*/ ++ ch_no = get_ch_num(&status); ++ if (ch_no < 0) { ++ dma_err("Ch no is invalid %x, abort!\n", ch_no); ++ return; ++ } ++ dma_dbg("Got Ch %x, new Status %x \n", ch_no, status); ++ i = get_ch_index(ch_no); ++ if (i < 0) { ++ dma_err("Invalid CH lnwc\n"); ++ return; ++ } ++ dma_dbg("Tx error interrupt %x, No %d Index %d \n", ++ status, ch_no, i); ++ lnwc = &lnw->ch[i]; ++ if (lnwc == NULL) { ++ dma_err("Null param lnwc\n"); ++ return; ++ } ++ spin_lock_bh(&lnwc->lock); ++ lnwc_handle_error(lnw, lnwc); ++ iowrite32((1 << lnwc->ch_id), ++ lnw->dma_base + CLEAR_ERR); ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), ++ lnw->dma_base + MASK_ERR); ++ spin_unlock_bh(&lnwc->lock); ++ } ++ dma_dbg("Exiting takslet... \n"); ++ return; ++} ++ ++static irqreturn_t lnw_dma2_interrupt(int irq, void *data) ++{ ++ struct lnw_device *lnw = data; ++ u32 status; ++ int call_tasklet = 0; ++ ++ /*will mask interrupt for now and schedule tasklet ++ tasklet shud unmask and clear*/ ++ status = ioread32(lnw->dma_base + STATUS_TFR); ++ status &= 0x03; ++ if (status) { ++ iowrite32((status << 8), lnw->dma_base + MASK_TFR); ++ call_tasklet = 1; ++ } ++ status = ioread32(lnw->dma_base + STATUS_ERR); ++ status &= 0x03; ++ if (status) { ++ iowrite32(MASK_INTR_REG(status), lnw->dma_base + MASK_ERR); ++ call_tasklet = 1; ++ } ++ ++ if (call_tasklet) ++ tasklet_schedule(&lnw->dma->tasklet); ++ ++ return IRQ_HANDLED; ++} ++ ++static void enable_dma2_interrupt(struct lnw_dma_chan *lnwc) ++{ ++ dma_dbg("Called for ch_id %d\n", lnwc->ch_id); ++ ++ iowrite32(REG_BIT0, lnwc->dma->dma_base + DMA_CFG); ++ /*en ch interrupts */ ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_TFR); ++ iowrite32(UNMASK_INTR_REG(lnwc->ch_id), lnwc->dma_base + MASK_ERR); ++ return; ++} ++ ++static void disable_dma2_interrupt(struct lnw_device *device) ++{ ++ u32 status = 0; ++ ++ /*todo*/ ++ dma_dbg(" called \n"); ++ status = 1; ++ return; ++ ++} ++ ++static int lnw_setup_dma2(struct pci_dev *pdev) ++{ ++ struct lnw_device *device = pci_get_drvdata(pdev); ++ struct lnwdma_device *dma = NULL; ++ int err, i; ++ ++ dma_dbg("setup_dma called \n"); ++ dma = kzalloc(sizeof(*dma), GFP_KERNEL); ++ if (NULL == dma) { ++ dma_err("kzalloc failed \n"); ++ err = -ENOMEM; ++ goto err_kzalloc; ++ } ++ device->dma = dma; ++ dma->pdev = pdev; ++ dma->dma_base = device->dma_base; ++ ++ /* DMA coherent memory pool for DMA descriptor allocations */ ++ dma->dma_pool = pci_pool_create("dma_desc_pool", pdev, ++ sizeof(struct lnw_dma_desc), ++ 32, 0); ++ if (NULL == dma->dma_pool) { ++ dma_err("pci_pool_create failed \n"); ++ err = -ENOMEM; ++ kfree(dma); ++ goto err_dma_pool; ++ } ++ ++ INIT_LIST_HEAD(&dma->common.channels); ++ ++ ++ /*init CH structures*/ ++ for (i = 0; i < MAX_CHAN; i++) { ++ struct lnw_dma_chan *lnwch = &dma->ch[i]; ++ ++ lnwch->chan.device = &dma->common; ++ lnwch->chan.cookie = 1; ++ lnwch->chan.chan_id = i; ++ lnwch->ch_id = get_ch_id(i); ++ dma_dbg("Init CH %d, ID %d \n", i, lnwch->ch_id); ++ ++ lnwch->dma_base = dma->dma_base; ++ lnwch->ch_regs = dma->dma_base + DMA_CH_SIZE * lnwch->ch_id; ++ lnwch->dma = dma; ++ spin_lock_init(&lnwch->lock); ++ ++ INIT_LIST_HEAD(&lnwch->active_list); ++ INIT_LIST_HEAD(&lnwch->queue); ++ INIT_LIST_HEAD(&lnwch->free_list); ++ ++ /*mask interrupts*/ ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_BLOCK); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_SRC_TRAN); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_DST_TRAN); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_ERR); ++ iowrite32(MASK_INTR_REG(lnwch->ch_id), ++ dma->dma_base + MASK_TFR); ++ ++ dma_dbg("Init CH %d, ID %d \n", i, lnwch->ch_id); ++ list_add_tail(&lnwch->chan.device_node, &dma->common.channels); ++ } ++ ++ /*init dma structure*/ ++ dma_cap_zero(dma->common.cap_mask); ++ dma_cap_set(DMA_MEMCPY, dma->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, dma->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, dma->common.cap_mask); ++ dma->common.dev = &pdev->dev; ++ dma->common.chancnt = MAX_CHAN; ++ ++ dma->common.device_alloc_chan_resources = ++ lnw_dma2_alloc_chan_resources; ++ dma->common.device_free_chan_resources = ++ lnw_dma2_free_chan_resources; ++ ++ dma->common.device_is_tx_complete = lnw_dma2_tx_is_complete; ++ dma->common.device_prep_dma_memcpy = lnw_dma2_prep_memcpy; ++ dma->common.device_issue_pending = lnw_dma2_issue_pending; ++ dma->common.device_prep_slave_sg = lnw_dma2_prep_slave_sg; ++ dma->common.device_terminate_all = lnw_dma2_terminate_all; ++ ++ /*enable dma cntrl*/ ++ iowrite32(REG_BIT0, dma->dma_base + DMA_CFG); ++ ++ disable_dma2_interrupt(device); ++ ++ /*register irq*/ ++ err = request_irq(pdev->irq, lnw_dma2_interrupt, ++ 0, lnw_dma2_pci.name, device); ++ if (0 != err) ++ goto err_irq; ++ ++ /*register device w/ engine*/ ++ err = dma_async_device_register(&dma->common); ++ if (0 != err) { ++ dma_err("device_register failed: %d \n", err); ++ goto err_engine; ++ } ++ tasklet_init(&dma->tasklet, dma_tasklet, (unsigned long)dma); ++ dma_dbg("...done\n"); ++ return 0; ++ ++err_engine: ++ free_irq(pdev->irq, device); ++err_irq: ++ pci_pool_destroy(dma->dma_pool); ++ kfree(dma); ++err_dma_pool: ++err_kzalloc: ++ dma_err("setup_dma failed: %d \n", err); ++ return err; ++ ++} ++ ++static void lnwdma_shutdown(struct pci_dev *pdev) ++{ ++ struct lnw_device *device = pci_get_drvdata(pdev); ++ ++ dma_dbg("shutdown called \n"); ++ dma_async_device_unregister(&device->dma->common); ++ pci_pool_destroy(device->dma->dma_pool); ++ if (device->dma_base) ++ iounmap(device->dma_base); ++ free_irq(pdev->irq, device); ++ return; ++} ++static int __devinit ++lnw_dma2_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct lnw_device *device = NULL; ++ u32 base_addr = 0, bar_size = 0; ++ int err = 0; ++ ++ dma_info("probe called for %x \n", pdev->device); ++ err = pci_enable_device(pdev); ++ if (err) ++ goto err_enable_device; ++ ++ err = pci_request_regions(pdev, lnw_dma2_pci.name); ++ if (err) ++ goto err_request_regions; ++ ++ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); ++ if (err) ++ goto err_set_dma_mask; ++ ++ err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); ++ if (err) ++ goto err_set_dma_mask; ++ ++ device = kzalloc(sizeof(*device), GFP_KERNEL); ++ if (!device) { ++ dma_err("kzalloc failed \n"); ++ err = -ENOMEM; ++ goto err_kzalloc; ++ } ++ device->pdev = pci_dev_get(pdev); ++ ++ base_addr = pci_resource_start(pdev, 0); ++ bar_size = pci_resource_len(pdev, 0); ++ dma_dbg("BAR0 %x Size %x \n", base_addr, bar_size); ++ device->dma_base = ioremap_nocache(base_addr, DMA_REG_SIZE); ++ if (!device->dma_base) { ++ dma_err("ioremap failed \n"); ++ err = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ pci_set_drvdata(pdev, device); ++ pci_set_master(pdev); ++ ++ err = lnw_setup_dma2(pdev); ++ if (err) ++ goto err_dma; ++ ++ return 0; ++ ++err_dma: ++ iounmap(device->dma_base); ++err_ioremap: ++ pci_dev_put(pdev); ++ kfree(device); ++err_kzalloc: ++err_set_dma_mask: ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++err_request_regions: ++err_enable_device: ++ dma_err("Probe failed %d\n", err); ++ return err; ++} ++ ++static void __devexit lnw_dma2_remove(struct pci_dev *pdev) ++{ ++ struct lnw_device *device = pci_get_drvdata(pdev); ++ ++ lnwdma_shutdown(pdev); ++ pci_dev_put(pdev); ++ kfree(device); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++} ++ ++static int __init lnw_dma2_init(void) ++{ ++ dma_info("LNW DMA Driver\n Version %s \n", LNW_DMA_DRIVER_VERSION); ++ return pci_register_driver(&lnw_dma2_pci); ++} ++fs_initcall(lnw_dma2_init); ++ ++static void __exit lnw_dma2_exit(void) ++{ ++ pci_unregister_driver(&lnw_dma2_pci); ++} ++module_exit(lnw_dma2_exit); ++ +Index: linux-2.6.33/include/linux/lnw_dma.h +=================================================================== +--- /dev/null ++++ linux-2.6.33/include/linux/lnw_dma.h +@@ -0,0 +1,166 @@ ++/* ++ * lnw_dma.c - Intel Langwell DMA Drivers ++ * ++ * Copyright (C) 2008i-09 Intel Corp ++ * Author: Vinod Koul <vinod.koul@intel.com> ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * ++ */ ++#ifndef __LNW_DMA_H__ ++#define __LNW_DMA_H__ ++ ++#include <linux/dmaengine.h> ++ ++/*DMA transaction width, src and dstn width would be same ++The DMA length must be width aligned, ++for 32 bit width the length must be 32 bit (4bytes) aligned only*/ ++enum lnw_dma_width { ++ LNW_DMA_WIDTH_8BIT = 0x0, ++ LNW_DMA_WIDTH_16BIT = 0x1, ++ LNW_DMA_WIDTH_32BIT = 0x2, ++}; ++ ++/*DMA mode configurations*/ ++enum lnw_dma_mode { ++ LNW_DMA_PER_TO_MEM = 0, /*periphral to memory configuration*/ ++ LNW_DMA_MEM_TO_PER, /*memory to periphral configuration*/ ++ LNW_DMA_MEM_TO_MEM, /*mem to mem confg (testing only)*/ ++}; ++ ++/*DMA handshaking*/ ++enum lnw_dma_hs_mode { ++ LNW_DMA_HW_HS = 0, /*HW Handshaking only*/ ++ LNW_DMA_SW_HS = 1, /*SW Handshaking not recommended*/ ++}; ++ ++/*Burst size configuration*/ ++enum lnw_dma_msize { ++ LNW_DMA_MSIZE_1 = 0x0, ++ LNW_DMA_MSIZE_4 = 0x1, ++ LNW_DMA_MSIZE_8 = 0x2, ++ LNW_DMA_MSIZE_16 = 0x3, ++ LNW_DMA_MSIZE_32 = 0x4, ++ LNW_DMA_MSIZE_64 = 0x5, ++}; ++ ++/** ++ * struct lnw_dma_slave - DMA slave structure ++ * ++ * @dma_dev: DMA master client ++ * @tx_reg: physical address of data register used for ++ * memory-to-peripheral transfers ++ * @rx_reg: physical address of data register used for ++ * peripheral-to-memory transfers ++ * @tx_width: tx register width ++ * @rx_width: rx register width ++ * @dirn: DMA trf direction ++ ++ * @cfg_hi: Platform-specific initializer for the CFG_HI register ++ * @cfg_lo: Platform-specific initializer for the CFG_LO register ++ ++ * @ tx_width: width of src and dstn ++ * @ hs_mode: SW or HW handskaking mode ++ * @ cfg_mode: Mode configuration, DMA mem to mem to dev & mem ++ */ ++struct lnw_dma_slave { ++ enum dma_data_direction dirn; ++ enum lnw_dma_width src_width; /*width of DMA src txn*/ ++ enum lnw_dma_width dst_width; /*width of DMA dst txn*/ ++ enum lnw_dma_hs_mode hs_mode; /*handshaking*/ ++ enum lnw_dma_mode cfg_mode; /*mode configuration*/ ++ enum lnw_dma_msize src_msize; /*size if src burst*/ ++ enum lnw_dma_msize dst_msize; /*size of dst burst*/ ++ dma_async_tx_callback callback; /*callback function*/ ++ void *callback_param; /*param for callback*/ ++}; ++ ++/*DMA channel control registers*/ ++union lnw_dma_ctl_lo { ++ struct { ++ u32 int_en:1; /*enable or disable interrupts*/ ++ /*should be 0*/ ++ u32 dst_tr_width:3; /*destination transfer width*/ ++ /*usually 32 bits = 010*/ ++ u32 src_tr_width:3; /*source transfer width*/ ++ /*usually 32 bits = 010*/ ++ u32 dinc:2; /*destination address inc/dec*/ ++ /*For mem:INC=00, Periphral NoINC=11*/ ++ u32 sinc:2; /*source address inc or dec, as above*/ ++ u32 dst_msize:3; /*destination burst transaction length*/ ++ /*always = 16 ie 011*/ ++ u32 src_msize:3; /*source burst transaction length*/ ++ /*always = 16 ie 011*/ ++ u32 reser1:3; ++ u32 tt_fc:3; /*transfer type and flow controller*/ ++ /*M-M = 000 ++ P-M = 010 ++ M-P = 001*/ ++ u32 dms:2; /*destination master select = 0*/ ++ u32 sms:2; /*source master select = 0*/ ++ u32 llp_dst_en:1; /*enable/disable destination LLP = 0*/ ++ u32 llp_src_en:1; /*enable/disable source LLP = 0*/ ++ u32 reser2:3; ++ } ctlx; ++ u32 ctl_lo; ++}; ++ ++union lnw_dma_ctl_hi { ++ struct { ++ u32 block_ts:12; /*block transfer size*/ ++ /*configured by DMAC*/ ++ u32 reser:20; ++ } ctlx; ++ u32 ctl_hi; ++ ++}; ++ ++/*DMA channel configuration registers*/ ++union lnw_dma_cfg_lo { ++ struct { ++ u32 reser1:5; ++ u32 ch_prior:3; /*channel priority = 0*/ ++ u32 ch_susp:1; /*channel suspend = 0*/ ++ u32 fifo_empty:1; /*FIFO empty or not R bit = 0*/ ++ u32 hs_sel_dst:1; /*select HW/SW destn handshaking*/ ++ /*HW = 0, SW = 1*/ ++ u32 hs_sel_src:1; /*select HW/SW src handshaking*/ ++ u32 reser2:6; ++ u32 dst_hs_pol:1; /*dest HS interface polarity*/ ++ u32 src_hs_pol:1; /*src HS interface polarity*/ ++ u32 max_abrst:10; /*max AMBA burst len = 0 (no sw limit*/ ++ u32 reload_src:1; /*auto reload src addr =1 if src is P*/ ++ u32 reload_dst:1; /*AR destn addr =1 if dstn is P*/ ++ } cfgx; ++ u32 cfg_lo; ++}; ++ ++union lnw_dma_cfg_hi { ++ struct { ++ u32 fcmode:1; /*flow control mode = 1*/ ++ u32 fifo_mode:1; /*FIFO mode select = 1*/ ++ u32 protctl:3; /*protection control = 0*/ ++ u32 rsvd:2; ++ u32 src_per:4; /*src hw HS interface*/ ++ u32 dst_per:4; /*dstn hw HS interface*/ ++ u32 reser2:17; ++ } cfgx; ++ u32 cfg_hi; ++}; ++ ++#endif /*__LNW_DMA_H__*/ +Index: linux-2.6.33/include/linux/intel_mid.h +=================================================================== +--- /dev/null ++++ linux-2.6.33/include/linux/intel_mid.h +@@ -0,0 +1,144 @@ ++/* ++ * intel_mid.h - Netlink multicast interface definition for OSPM. ++ * ++ * Copyright (C) 2009 Intel Corp ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * Authors: Sujith Thomas ++ * Rajeev D Muralidhar ++ * Vishwesh M Rudramuni ++ * Nithish Mahalingam ++ * Contact information: ++ * Sujith Thomas <sujith.thomas@intel.com> ++ * Rajeev D Muralidhar <rajeev.d.muralidhar@intel.com> ++ * Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com> ++ * Nithish Mahalingam <nithish.mahalingam@intel.com> ++ * ++ */ ++ ++#ifndef INTEL_MID_H ++#define INTEL_MID_H ++ ++#define PMU1_MAX_DEVS 2 ++#define PMU2_MAX_DEVS 12 ++#define PERIPH_MAX_DEVS 3 ++#define MAX_DEVICES (PMU1_MAX_DEVS + PMU2_MAX_DEVS + PERIPH_MAX_DEVS) ++#define WAKE_CAPABLE 0x80000000 ++ ++struct pci_dev_info { ++ u16 vendor_id; ++ u16 device_id; ++ u16 log_subsysid; ++ u16 phy_susbsysid; ++ u32 capability; ++ struct pci_dev *dev_driver; ++ char *dev_name; ++}; ++ ++struct mid_ospm { ++ u32 *pmu1_base; ++ u32 *pmu1_pm_base; ++ void __iomem *pmu2_base; ++ u32 *pm_table_base; ++ u32 pmu1_sub_systems; ++ u32 pmu2_sub_systems; ++ u32 pmu_wake_cfg; ++ u32 pmu_wake_ss_states; ++ u32 perepheral_sub_systems; ++ int pmu2_states; ++ int platform_sx_state; ++ int s0ix_retry_enb; ++ int fast_retry_exit; ++ u32 pmode; ++}; ++ ++extern struct pci_dev_info platform_pci_devices[MAX_DEVICES]; ++extern unsigned long g_intel_mid_wakeup_address; ++ ++enum pmu_ss_state { ++ SS_STATE_D0I0 = 0, ++ SS_STATE_D0I1 = 1, ++ SS_STATE_D0I2 = 2, ++ SS_STATE_D0I3 = 3 ++}; ++ ++enum eospm_events { ++ OSPM_EVENT_SUBSYS_INACTIVITY, ++ OSPM_EVENT_SUBSYS_WAKE, ++ OSPM_EVENT_SUBSYS_START_PLAY, ++ OSPM_EVENT_SUBSYS_STOP_PLAY, ++ OSPM_EVENT_CMD_SUCCESS, ++ OSPM_EVENT_CMD_ERROR, ++ OSPM_EVENT_CMD_NO_C6_ERROR, ++ OSPM_EVENT_AUDIO_BUF_EMPTY, ++ OSPM_EVENT_AUDIO_BUF_FULL, ++ OSPM_EVENT_THERMAL_AUX0, ++ OSPM_EVENT_THERMAL_AUX1, ++ OSPM_EVENT_THERMAL_CRITICAL, ++ OSPM_EVENT_THERMAL_DEV_FAULT, ++ __OSPM_EVENT_COUNT, ++}; ++ ++#define AUDIO_SUBSYTEM_ID 25 ++#define MID_S0I1_STATE 1 ++#define MID_S0I3_STATE 3 ++/* Thermal device Id */ ++#define TEMP_DEV_ID1 40 ++#define TEMP_DEV_ID2 41 ++#define TEMP_DEV_ID3 42 ++ ++/* First 32 (0-31) originators are subsystems ++ Next 8 (0-7) are cmd IDs */ ++#define OSPM_CMDID_OFFSET 32 ++#define OSPM_MAX_CMD_ID 8 ++ ++struct ospm_genl_event { ++ u32 orig; ++ enum eospm_events event; ++}; ++ ++/* attributes of ospm_genl_family */ ++enum { ++ OSPM_GENL_ATTR_UNSPEC, ++ OSPM_GENL_ATTR_EVENT, /* OSPM event info needed by user space */ ++ __OSPM_GENL_ATTR_MAX, ++}; ++#define OSPM_GENL_ATTR_MAX (__OSPM_GENL_ATTR_MAX - 1) ++ ++/* commands supported by the ospm_genl_family */ ++ ++enum { ++ OSPM_GENL_CMD_UNSPEC, ++ OSPM_GENL_CMD_EVENT, /* kernel->user notifications for OSPM events */ ++ __OSPM_GENL_CMD_MAX, ++}; ++#define OSPM_GENL_CMD_MAX (__OSPM_GENL_CMD_MAX - 1) ++ ++#define OSPM_GENL_FAMILY_NAME "ospm_event" ++#define OSPM_GENL_VERSION 0x01 ++#define OSPM_GENL_MCAST_GROUP_NAME "ospm_mc_group" ++ ++int ospm_generate_netlink_event(u32 orig, enum eospm_events event); ++int ospm_event_genetlink_init(void); ++void ospm_event_genetlink_exit(void); ++ ++extern void intel_mid_reserve_bootmem(void); ++extern unsigned long g_intel_mid_wakeup_address; ++extern void find_pci_info(u32 device_id, u32 vendor_id, u32 *index); ++extern int s0ix_non_bsp_init(void); ++ ++#endif |