Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933130Ab2EQC0K (ORCPT ); Wed, 16 May 2012 22:26:10 -0400 Received: from db3ehsobe005.messaging.microsoft.com ([213.199.154.143]:19191 "EHLO db3outboundpool.messaging.microsoft.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1760528Ab2EQC0H convert rfc822-to-8bit (ORCPT ); Wed, 16 May 2012 22:26:07 -0400 X-SpamScore: -3 X-BigFish: VPS-3(zz9371I542M853kzz1202hzz8275bhz2dh2a8h668h839h944hd25h) X-Forefront-Antispam-Report: CIP:137.71.25.57;KIP:(null);UIP:(null);IPV:NLI;H:nwd2mta2.analog.com;RD:nwd2mail11.analog.com;EFVD:NLI From: "Wu, Aaron" To: "Wu, Aaron" , "arnd@arndb.de" , "gregkh@linuxfoundation.org" , "linux-kernel@vger.kernel.org" , "vapier@gentoo.org" , "Zhang, Sonic" Date: Wed, 16 May 2012 22:26:41 -0400 Subject: RE: [PATCH] add blackfin SPORT common driver Thread-Topic: [PATCH] add blackfin SPORT common driver Thread-Index: Ac0jjoK5DpxnagntS+yLY1Diuhe/ZgQRabIQ Message-ID: References: <1335432293-5002-1-git-send-email-Aaron.Wu@analog.com> In-Reply-To: <1335432293-5002-1-git-send-email-Aaron.Wu@analog.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: zh-CN, en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 8BIT MIME-Version: 1.0 X-OriginatorOrg: analog.com Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 32206 Lines: 997 Hi Greg/Arnd, Do you have a minute reviewing this patch and give some comments, and is there a plan to merge it? Best regards, Aaron -----Original Message----- From: Aaron Wu [mailto:Aaron.Wu@analog.com] Sent: Thursday, April 26, 2012 5:25 PM To: arnd@arndb.de; gregkh@linuxfoundation.org; linux-kernel@vger.kernel.org; vapier@gentoo.org; Zhang, Sonic Cc: Wu, Aaron Subject: [PATCH] add blackfin SPORT common driver this driver is for blackfin SPORT common driver, it's for direct acess from userspace application. Signed-off-by: Aaron Wu --- drivers/char/Kconfig | 12 + drivers/char/Makefile | 1 + drivers/char/bfin-sport.c | 922 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 935 insertions(+), 0 deletions(-) create mode 100644 drivers/char/bfin-sport.c diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ee94686..b6591f4 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -82,6 +82,18 @@ config BFIN_OTP If unsure, it is safe to say Y. +config BFIN_SPORT + tristate "Blackfin SPORT driver for direct raw access" + depends on BLACKFIN + default n + help + This is a generic character interface to the SPORT peripheral that + exists on many Blackfin parts. It is a standalone driver that is + only used for direct userspace access to the device and is not used + in conjunction with any other driver. + + If unsure, then say N. + config BFIN_OTP_WRITE_ENABLE bool "Enable writing support of OTP pages" depends on BFIN_OTP diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 0dc5d7c..7e757e9 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_SGI_MBCS) += mbcs.o obj-$(CONFIG_BFIN_OTP) += bfin-otp.o +obj-$(CONFIG_BFIN_SPORT) += bfin-sport.o obj-$(CONFIG_PRINTER) += lp.o diff --git a/drivers/char/bfin-sport.c b/drivers/char/bfin-sport.c new file mode 100644 index 0000000..702a33d --- /dev/null +++ b/drivers/char/bfin-sport.c @@ -0,0 +1,922 @@ +/* + * simple char interface to Blackfin SPORT peripheral + * + * Copyright 2004-2012 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#define DRV_NAME "bfin_sport_raw" +#define pr_fmt(fmt) (DRV_NAME ": " fmt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static LIST_HEAD(sport_list); + +static struct sport_dev *bfin_sport_dev; + +struct sport_dev { + struct list_head list; + struct device *dev; + struct miscdevice misc; + char name[16]; /*bfin_sport#*/ + const unsigned short *pin_req; + + int dma_rx_chan; + int dma_tx_chan; + + int rx_irq; + unsigned char *rx_buf; /* Buffer store the received data */ + int rx_len; /* How many bytes will be received */ + int rx_received; /* How many bytes has been received */ + + int tx_irq; + const unsigned char *tx_buf; + int tx_len; + int tx_sent; + + int err_irq; + + struct mutex mutex; + unsigned int open_count; + + struct completion c; + + volatile struct sport_register __iomem *regs; + resource_size_t reg_base; + unsigned long reg_len; + struct sport_config config; +}; + +static int maybe_request_irq(int irq, irq_handler_t handler, + unsigned long flags, const char *name, struct sport_dev *dev) +{ + int ret = 0; + + if (irq == -1) + return ret; + + ret = request_irq(irq, handler, flags, name, dev); + if (ret) + dev_err(dev->dev, "unable to request irq %s\n", name); + + return ret; +} + +static void maybe_free_irq(int irq, void *dev) +{ + if (irq != -1) + free_irq(irq, dev); +} + +static irqreturn_t dma_rx_irq_handler(int irq, void *dev_id); +static irqreturn_t dma_tx_irq_handler(int irq, void *dev_id); + +/* note: multichannel is in unit of 8 channels, + tdm_count is # channels NOT / 8 ! +*/ +static int sport_set_multichannel(volatile struct sport_register *regs, + int tdm_count, int packed, int frame_delay) +{ + if (tdm_count) { + int shift = 32 - tdm_count; + unsigned int mask = (0xffffffff >> shift); + + regs->mcmc1 = ((tdm_count >> 3) - 1) << 12; /* WSIZE bits */ + regs->mcmc2 = (frame_delay << 12) | MCMEN | + (packed ? (MCDTXPE | MCDRXPE) : 0); + + regs->mtcs0 = regs->mrcs0 = mask; + } else { + regs->mcmc1 = regs->mcmc2 = 0; + regs->mtcs0 = regs->mrcs0 = 0; + } + + regs->mtcs1 = regs->mtcs2 = regs->mtcs3 = 0; + regs->mrcs1 = regs->mrcs2 = regs->mrcs3 = 0; + + SSYNC(); + + return 0; +} + +static uint16_t sport_wordsize(struct sport_dev *dev, int word_len) +{ + uint16_t wordsize = 0; + + if (word_len <= 8) + wordsize = WDSIZE_8; + else if (word_len <= 16) + wordsize = WDSIZE_16; + else if (word_len <= 32) + wordsize = WDSIZE_32; + else + dev_err(dev->dev, "word_len of %d is invalid\n", word_len); + + return wordsize; +} + +static irqreturn_t dma_rx_irq_handler(int irq, void *dev_id) +{ + struct sport_dev *dev = dev_id; + int status; + + dev_dbg(dev->dev, "%s enter\n", __func__); + status = dev->regs->mcmc2; + if (status & MCMEN) + dev->regs->rcr1 &= ~RSPEN; + dev->regs->rcr1 &= ~RSPEN; + SSYNC(); + disable_dma(dev->dma_rx_chan); + + complete(&dev->c); + + clear_dma_irqstat(dev->dma_rx_chan); + return IRQ_HANDLED; +} + +static irqreturn_t dma_tx_irq_handler(int irq, void *dev_id) +{ + struct sport_dev *dev = dev_id; + unsigned int status; + + dev_dbg(dev->dev, "%s enter\n", __func__); + if (dev->err_irq != -1) + disable_irq(dev->err_irq); + status = get_dma_curr_irqstat(dev->dma_tx_chan); + while (status & DMA_RUN) { + dev_dbg(dev->dev, "status:0x%04x\n", status); + status = get_dma_curr_irqstat(dev->dma_tx_chan); + } + status = dev->regs->stat; + while (!(status & TXHRE)) { + dev_dbg(dev->dev, "status:%x\n", status); + udelay(1); + status = dev->regs->stat; + } + /* Wait for the last byte sent out */ + udelay(500); + dev_dbg(dev->dev, "%s status:%x\n", __func__, status); + + dev->regs->tcr1 &= ~TSPEN; + status = dev->regs->mcmc2; + if (status & MCMEN) + dev->regs->rcr1 &= ~RSPEN; + SSYNC(); + if (dev->err_irq != -1) + enable_irq(dev->err_irq); + disable_dma(dev->dma_tx_chan); + + complete(&dev->c); + + /* Clear the interrupt status */ + clear_dma_irqstat(dev->dma_tx_chan); + + return IRQ_HANDLED; +} + + +static inline void sport_rx_read(struct sport_dev *dev) +{ + struct sport_config *cfg = &dev->config; + + if (cfg->word_len <= 8) + while (dev->rx_received < dev->rx_len && + (dev->regs->stat & RXNE)) { + u8 *buf = (void *)dev->rx_buf + dev->rx_received; + *buf = dev->regs->rx16; + dev->rx_received += 1; + } + else if (cfg->word_len <= 16) + while (dev->rx_received < dev->rx_len && + (dev->regs->stat & RXNE)) { + u16 *buf = (void *)dev->rx_buf + dev->rx_received; + *buf = dev->regs->rx16; + dev->rx_received += 2; + } + else + while (dev->rx_received < dev->rx_len && + (dev->regs->stat & RXNE)) { + u32 *buf = (void *)dev->rx_buf + dev->rx_received; + *buf = bfin_read_sport_rx32(dev->regs); + dev->rx_received += 4; + } +} + +static irqreturn_t sport_rx_handler(int irq, void *dev_id) +{ + struct sport_dev *dev = dev_id; + sport_rx_read(dev); + + if (dev->rx_received >= dev->rx_len) { + dev->regs->rcr1 &= ~RSPEN; + complete(&dev->c); + } + + return IRQ_HANDLED; +} + +static inline void sport_tx_write(struct sport_dev *dev) +{ + struct sport_config *cfg = &dev->config; + + if (cfg->word_len <= 8) + while (dev->tx_sent < dev->tx_len && + !(dev->regs->stat & TXF)) { + u8 *buf = (void *)dev->tx_buf + dev->tx_sent; + dev->regs->tx16 = *buf; + dev->tx_sent += 1; + } + else if (cfg->word_len <= 16) + while (dev->tx_sent < dev->tx_len && + !(dev->regs->stat & TXF)) { + u16 *buf = (void *)dev->tx_buf + dev->tx_sent; + dev->regs->tx16 = *buf; + dev->tx_sent += 2; + } + else + while (dev->tx_sent < dev->tx_len && + !(dev->regs->stat & TXF)) { + u32 *buf = (void *)dev->tx_buf + dev->tx_sent; + dev->regs->tx32 = *buf; + dev->tx_sent += 4; + } +} + +static irqreturn_t sport_tx_handler(int irq, void *dev_id) +{ + struct sport_dev *dev = dev_id; + + if (dev->config.mode != NDSO_MODE) { + if (dev->tx_sent < dev->tx_len) + sport_tx_write(dev); + } + + if (dev->tx_len != 0 && dev->tx_sent >= dev->tx_len + && dev->config.int_clk) { + unsigned int stat; + + stat = dev->regs->stat; + while (!(stat & TXHRE)) { + dev_dbg(dev->dev, "%s: stat:%x\n", __func__, stat); + udelay(1); + stat = dev->regs->stat; + } + udelay(500); + dev->regs->tcr1 &= ~TSPEN; + SSYNC(); + dev_dbg(dev->dev, "%s: stat:%x\n", __func__, stat); + complete(&dev->c); + } + + return IRQ_HANDLED; +} + + +static irqreturn_t sport_err_handler(int irq, void *dev_id) +{ + struct sport_dev *dev = dev_id; + uint16_t status; + + dev_dbg(dev->dev, "%s enter\n", __func__); + status = dev->regs->stat; + + if (status & (TOVF | TUVF | ROVF | RUVF)) { + dev->regs->stat = (status & (TOVF | TUVF | ROVF | RUVF)); + if (dev->config.dma_enabled) { + disable_dma(dev->dma_rx_chan); + disable_dma(dev->dma_tx_chan); + } + dev->regs->tcr1 &= ~TSPEN; + dev->regs->rcr1 &= ~RSPEN; + SSYNC(); + + if (!dev->config.dma_enabled && !dev->config.int_clk) { + if (status & TUVF) + complete(&dev->c); + } else + dev_warn(dev->dev, "sport %p status error:%s%s%s%s\n", + dev->regs, + (status & TOVF) ? " TOVF" : "", + (status & TUVF) ? " TUVF" : "", + (status & ROVF) ? " ROVF" : "", + (status & RUVF) ? " RUVF" : ""); + } + + /* XXX: should we always complete here and have read/write error ? */ + + return IRQ_HANDLED; +} + +static inline struct sport_dev *to_sport_dev(struct file *filp) +{ + return filp->private_data; +} + +/* + * Open and close + */ +static int sport_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + int minor; + struct sport_dev *dev; + + dev_dbg(dev->dev, "%s enter\n", __func__); + + minor = MINOR(inode->i_rdev); + list_for_each_entry(dev, &sport_list, list) + if (dev->misc.minor == minor) { + filp->private_data = dev; + break; + } + + mutex_init(&dev->mutex); + + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + + if (dev->open_count++) + goto done; + + memset(&dev->config, 0, sizeof(dev->config)); + + dev->rx_buf = NULL; + dev->rx_len = 0; + dev->rx_received = 0; + dev->tx_buf = NULL; + dev->tx_len = 0; + dev->tx_sent = 0; + init_completion(&dev->c); + + dev->regs = devm_ioremap(dev->dev, dev->reg_base, dev->reg_len); + if (!dev->regs) { + dev_err(dev->dev, "unable to map regs\n"); + goto fail; + } + + ret = maybe_request_irq(dev->tx_irq, sport_tx_handler, IRQF_SHARED, + KBUILD_MODNAME "-tx", dev); + if (ret) + goto fail; + + ret = maybe_request_irq(dev->rx_irq, sport_rx_handler, IRQF_SHARED, + KBUILD_MODNAME "-rx", dev); + if (ret) + goto fail1; + ret = maybe_request_irq(dev->err_irq, sport_err_handler, 0, + KBUILD_MODNAME "-err", dev); + if (ret) + goto fail2; + + ret = peripheral_request_list(dev->pin_req, KBUILD_MODNAME); + if (ret) { + dev_err(dev->dev, "requesting peripherals failed\n"); + goto fail3; + } + +done: + mutex_unlock(&dev->mutex); + return 0; + +fail3: + free_irq(dev->err_irq, dev); +fail2: + free_irq(dev->rx_irq, dev); +fail1: + free_irq(dev->tx_irq, dev); +fail: + mutex_unlock(&dev->mutex); + return ret; +} + +static int sport_release(struct inode *inode, struct file *filp) +{ + struct sport_dev *dev = to_sport_dev(filp); + dev_dbg(dev->dev, "%s enter\n", __func__); + + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + + if (--dev->open_count) + goto done; + + dev->regs->tcr1 &= ~TSPEN; + dev->regs->rcr1 &= ~RSPEN; + + if (dev->config.dma_enabled) { + free_dma(dev->dma_rx_chan); + free_dma(dev->dma_tx_chan); + } else { + maybe_free_irq(dev->tx_irq, dev); + maybe_free_irq(dev->rx_irq, dev); + } + maybe_free_irq(dev->err_irq, dev); + + peripheral_free_list(dev->pin_req); + + devm_iounmap(dev->dev, (void *)dev->regs); +done: + mutex_unlock(&dev->mutex); + return 0; +} + +static void sport_ndso_tx_write(struct sport_dev *dev) +{ + struct sport_config *cfg = &dev->config; + int dummy; + + dev->regs->tcr1 |= TSPEN; + dev->regs->rcr1 |= RSPEN; + if (cfg->word_len <= 8) + while (dev->tx_sent < dev->tx_len) { + u8 *buf = (void *)dev->tx_buf + dev->tx_sent; + dev->regs->tx16 = *buf; + dev->tx_sent += 1; + while (!(dev->regs->stat & RXNE)) + cpu_relax(); + dummy = dev->regs->rx16; + } + else if (cfg->word_len <= 16) + while (dev->tx_sent < dev->tx_len) { + u16 *buf = (void *)dev->tx_buf + dev->tx_sent; + dev->regs->tx16 = *buf; + dev->tx_sent += 2; + while (!(dev->regs->stat & RXNE)) + cpu_relax(); + dummy = dev->regs->rx16; + } + else + while (dev->tx_sent < dev->tx_len) { + u32 *buf = (void *)dev->tx_buf + dev->tx_sent; + dev->regs->tx32 = *buf; + dev->tx_sent += 4; + while (!(dev->regs->stat & RXNE)) + cpu_relax(); + dummy = dev->regs->rx32; + } + dev->regs->tcr1 &= ~TSPEN; + dev->regs->rcr1 &= ~RSPEN; +} + + +static void sport_ndso_rx_read(struct sport_dev *dev) +{ + struct sport_config *cfg = &dev->config; + + dev->regs->tcr1 |= TSPEN; + dev->regs->rcr1 |= RSPEN; + if (cfg->word_len <= 8) + while (dev->rx_received < dev->rx_len) { + u8 *buf = (void *)dev->rx_buf + dev->rx_received; + dev->regs->tx16 = 0x00; + while (!(dev->regs->stat & RXNE)) + cpu_relax(); + *buf = dev->regs->rx16; + dev->rx_received += 1; + } + else if (cfg->word_len <= 16) + while (dev->rx_received < dev->rx_len) { + u16 *buf = (void *)dev->rx_buf + dev->rx_received; + dev->regs->tx16 = 0x0000; + while (!(dev->regs->stat & RXNE)) + cpu_relax(); + *buf = dev->regs->rx16; + dev->rx_received += 2; + } + else + while (dev->rx_received < dev->rx_len) { + u32 *buf = (void *)dev->rx_buf + dev->rx_received; + dev->regs->tx32 = 0x0000; + while (!(dev->regs->stat & RXNE)) + cpu_relax(); + *buf = dev->regs->rx32; + dev->rx_received += 4; + } + dev->regs->tcr1 &= ~TSPEN; + dev->regs->rcr1 &= ~RSPEN; +} + + +static ssize_t sport_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct sport_dev *dev = to_sport_dev(filp); + struct sport_config *cfg = &dev->config; + int status; + + dev_dbg(dev->dev, "%s count:%ld\n", __func__, count); + + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + + if (cfg->dma_enabled) { + int word_bytes = (cfg->word_len + 7) / 8; + uint16_t dma_config, xcount, ycount; + + if (word_bytes == 3) + word_bytes = 4; + + /* Invalidate the buffer */ + invalidate_dcache_range((unsigned long)buf, + (unsigned long)(buf + count)); + dev_dbg(dev->dev, "DMA mode read\n"); + dma_cache_sync(dev->dev, buf, count, DMA_FROM_DEVICE); + /* Configure dma */ + dma_config = WNR | RESTART | DI_EN | + sport_wordsize(dev, cfg->word_len); + xcount = count / word_bytes; + ycount = 0; + if ((count / word_bytes) > 0x8000) { + ycount = (count / word_bytes) >> 15; + xcount = 0x8000; + dma_config |= DMA2D; + } + set_dma_start_addr(dev->dma_rx_chan, (unsigned long)buf); + set_dma_x_count(dev->dma_rx_chan, xcount); + set_dma_x_modify(dev->dma_rx_chan, word_bytes); + if (ycount > 0) { + set_dma_y_count(dev->dma_rx_chan, ycount); + set_dma_y_modify(dev->dma_rx_chan, word_bytes); + } + set_dma_config(dev->dma_rx_chan, dma_config); + + enable_dma(dev->dma_rx_chan); + } else { + dev->rx_buf = buf; + dev->rx_len = count; + dev->rx_received = 0; + } + + if (dev->config.mode == NDSO_MODE) { + sport_ndso_rx_read(dev); + goto out; + } + + dev->regs->rcr1 |= RSPEN; + status = dev->regs->mcmc2; + if (status & MCMEN) + dev->regs->tcr1 |= TSPEN; + SSYNC(); + + if (wait_for_completion_interruptible(&dev->c)) { + dev_dbg(dev->dev, "Receive a signal to interrupt\n"); + count = -ERESTARTSYS; + /* fall through */ + } +out: + dev_dbg(dev->dev, "Complete called in dma rx irq handler\n"); + mutex_unlock(&dev->mutex); + + return count; +} + +static ssize_t sport_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int status; + struct sport_dev *dev = to_sport_dev(filp); + struct sport_config *cfg = &dev->config; + dev_dbg(dev->dev, "%s count:%ld dma_tx_chan:%d\n", + __func__, count, dev->dma_tx_chan); + + if (mutex_lock_interruptible(&dev->mutex)) + return -ERESTARTSYS; + + /* Configure dma to start transfer */ + if (cfg->dma_enabled) { + uint16_t dma_config, xcount, ycount; + int word_bytes = (cfg->word_len + 7) / 8; + + if (word_bytes == 3) + word_bytes = 4; + + dev_dbg(dev->dev, "DMA mode\n"); + dma_cache_sync(dev->dev, (void *)buf, count, DMA_TO_DEVICE); + + /* Configure dma */ + dma_config = RESTART | DI_EN | + sport_wordsize(dev, cfg->word_len); + xcount = count / word_bytes; + ycount = 0; + if ((count / word_bytes) > 0x8000) { + ycount = (count / word_bytes) >> 15; + xcount = 0x8000; + dma_config |= DMA2D; + } + set_dma_start_addr(dev->dma_tx_chan, (unsigned long)buf); + set_dma_x_count(dev->dma_tx_chan, xcount); + set_dma_x_modify(dev->dma_tx_chan, word_bytes); + if (ycount > 0) { + set_dma_y_count(dev->dma_tx_chan, ycount); + set_dma_y_modify(dev->dma_tx_chan, word_bytes); + } + set_dma_config(dev->dma_tx_chan, dma_config); + + /* dma irq should not be handled before sport is enabled */ + disable_irq(dev->tx_irq); + enable_dma(dev->dma_tx_chan); + dev->regs->tcr1 |= TSPEN; + enable_irq(dev->tx_irq); + } else { + /* Configure parameters to start PIO transfer */ + dev->tx_buf = buf; + dev->tx_len = count; + dev->tx_sent = 0; + if (dev->config.mode == NDSO_MODE) { + sport_ndso_tx_write(dev); + goto out; + } + sport_tx_write(dev); + dev->regs->tcr1 |= TSPEN; + } + + status = dev->regs->mcmc2; + if (status & MCMEN) + dev->regs->rcr1 |= RSPEN; + dev->regs->tcr1 |= TSPEN; + SSYNC(); + + dev_dbg(dev->dev, "wait for transfer finished\n"); + if (wait_for_completion_interruptible(&dev->c)) { + dev_dbg(dev->dev, "Receive a signal to interrupt\n"); + count = -ERESTARTSYS; + /* fall through */ + } + dev_dbg(dev->dev, "waiting over\n"); +out: + mutex_unlock(&dev->mutex); + + return count; +} + +static int sport_configure(struct sport_dev *dev, struct sport_config *config) +{ + unsigned int tcr1, tcr2, rcr1, rcr2; + unsigned int clkdiv, fsdiv; + struct sport_config *old_cfg = &dev->config; + + tcr1 = tcr2 = rcr1 = rcr2 = 0; + clkdiv = fsdiv = 0; + + if ((old_cfg->dma_enabled == 0) && (config->dma_enabled)) { + int ret; + free_irq(dev->tx_irq, dev); + free_irq(dev->rx_irq, dev); + + /* Request rx dma and set irq handler */ + ret = request_dma(dev->dma_rx_chan, "sport_rx_dma_chan"); + if (ret) { + pr_err("unable to request sport rx dma channel\n"); + return ret; + } + set_dma_callback(dev->dma_rx_chan, dma_rx_irq_handler, dev); + + /* Request tx dma and set irq handler */ + ret = request_dma(dev->dma_tx_chan, "sport_tx_dma_chan"); + if (ret) { + pr_err("unable to request sport tx dma channel\n"); + return ret; + } + set_dma_callback(dev->dma_tx_chan, dma_tx_irq_handler, dev); + } + memcpy(old_cfg, config, sizeof(*config)); + + if ((dev->regs->tcr1 & TSPEN) || (dev->regs->rcr1 & RSPEN)) + return -EBUSY; + + if (config->mode == TDM_MODE) { + if (config->channels & 0x7 || config->channels > 32) + return -EINVAL; + + sport_set_multichannel(dev->regs, config->channels, 1, + config->frame_delay); + } else { + tcr1 |= (config->lsb_first << 4) | (config->fsync << 10) | + (config->data_indep << 11) | (config->act_low << 12) | + (config->late_fsync << 13) | (config->tckfe << 14); + if (config->sec_en) + tcr2 |= TXSE; + + rcr1 |= (config->lsb_first << 4) | (config->fsync << 10) | + (config->data_indep << 11) | (config->act_low << 12) | + (config->late_fsync << 13) | (config->tckfe << 14); + if (config->sec_en) + rcr2 |= RXSE; + } + + /* Using internal clock */ + if (config->int_clk) { + u_long sclk = get_sclk(); + + if (config->serial_clk < 0 || config->serial_clk > sclk / 2) + return -EINVAL; + clkdiv = sclk / (2 * config->serial_clk) - 1; + fsdiv = config->serial_clk / config->fsync_clk - 1; + + tcr1 |= (ITCLK | ITFS); + rcr1 |= (IRCLK | IRFS); + } + + /* Setting data format */ + tcr1 |= (config->data_format << 2); /* Bit TDTYPE */ + rcr1 |= (config->data_format << 2); /* Bit TDTYPE */ + if (config->word_len >= 3 && config->word_len <= 32) { + tcr2 |= config->word_len - 1; + rcr2 |= config->word_len - 1; + } else + return -EINVAL; + + dev->regs->rcr1 = rcr1; + dev->regs->rcr2 = rcr2; + dev->regs->rclkdiv = clkdiv; + dev->regs->rfsdiv = fsdiv; + dev->regs->tcr1 = tcr1; + dev->regs->tcr2 = tcr2; + dev->regs->tclkdiv = clkdiv; + dev->regs->tfsdiv = fsdiv; + SSYNC(); + + dev_dbg(dev->dev, "tcr1:0x%x, tcr2:0x%x, rcr1:0x%x, rcr2:0x%x\n" + "mcmc1:0x%x, mcmc2:0x%x\n", + dev->regs->tcr1, dev->regs->tcr2, + dev->regs->rcr1, dev->regs->rcr2, + dev->regs->mcmc1, dev->regs->mcmc2); + + return 0; +} +static long sport_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct sport_dev *dev = to_sport_dev(filp); + struct sport_config config; + + dev_dbg(dev->dev, "%s: enter, arg:0x%lx\n", __func__, arg); + switch (cmd) { + case SPORT_IOC_CONFIG: + if (copy_from_user(&config, (void *)arg, sizeof(config))) + return -EFAULT; + if (sport_configure(dev, &config) < 0) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations bfin_sport_fops = { + .owner = THIS_MODULE, + .read = sport_read, + .write = sport_write, + .unlocked_ioctl = sport_ioctl, + .open = sport_open, + .release = sport_release, +}; + +#ifdef CONFIG_PM +static int bfin_sport_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int bfin_sport_resume(struct platform_device *dev) +{ + return 0; +} + +#else +#define bfin_sport_suspend NULL +#define bfin_sport_resume NULL +#endif + +static int __devinit bfin_sport_probe(struct platform_device *pdev) +{ + struct sport_dev *dev; + struct miscdevice *misc; + int ret; + struct resource *res; + unsigned short *pdata; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &pdev->dev; + snprintf(dev->name, sizeof(dev->name), "sport%i", pdev->id); + ret = -ENOENT; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev->dev, "no regs found\n"); + goto err; + } + dev->reg_base = res->start; + dev->reg_len = resource_size(res); + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev->dev, "no platform data found\n"); + goto err; + } + dev->pin_req = pdata; + + misc = &dev->misc; + misc->minor = MISC_DYNAMIC_MINOR; + misc->name = dev->name; + misc->fops = &bfin_sport_fops; + + ret = misc_register(&dev->misc); + if (ret) { + dev_err(dev->dev, "unable to register a misc device\n"); + goto err; + } + printk(KERN_ERR "misc = 0x%x\n", misc); + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + dev->dma_tx_chan = res ? res->start : -1; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + dev->dma_rx_chan = res ? res->start : -1; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + dev->tx_irq = res ? res->start : -1; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + dev->rx_irq = res ? res->start : -1; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + dev->err_irq = res ? res->start : -1; + + list_add(&dev->list, &sport_list); + platform_set_drvdata(pdev, dev); + + return 0; + +err: + kfree(dev); + return ret; +} + +static int __devexit bfin_sport_remove(struct platform_device *pdev) +{ + struct sport_dev *dev = platform_get_drvdata(pdev); + int ret; + + ret = misc_deregister(&dev->misc); + if (ret) + return ret; + + kfree(dev); + + return 0; +} + +static struct platform_driver bfin_sport_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = bfin_sport_probe, + .remove = __devexit_p(bfin_sport_remove), + .suspend = bfin_sport_suspend, + .resume = bfin_sport_resume, +}; + +static int __init bfin_sport_init(void) +{ + return platform_driver_register(&bfin_sport_driver); +} +module_init(bfin_sport_init); + +static void __exit bfin_sport_exit(void) +{ + platform_driver_unregister(&bfin_sport_driver); +} +module_exit(bfin_sport_exit); + +MODULE_AUTHOR("Roy Huang "); +MODULE_DESCRIPTION("Common Blackfin SPORT driver"); +MODULE_LICENSE("GPL"); -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/