forked from Minki/linux
Merge 4.0-rc3 into tty-testing
This resolves a merge issue in drivers/tty/serial/8250/8250_pci.c Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
commit
becba85f0e
@ -43,10 +43,6 @@ config EARLY_PRINTK
|
||||
with klogd/syslogd or the X server. You should normally N here,
|
||||
unless you want to debug such a crash.
|
||||
|
||||
config EARLY_PRINTK_INTEL_MID
|
||||
bool "Early printk for Intel MID platform support"
|
||||
depends on EARLY_PRINTK && X86_INTEL_MID
|
||||
|
||||
config EARLY_PRINTK_DBGP
|
||||
bool "Early printk via EHCI debug port"
|
||||
depends on EARLY_PRINTK && PCI
|
||||
|
@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
|
||||
#define SFI_MTMR_MAX_NUM 8
|
||||
#define SFI_MRTC_MAX 8
|
||||
|
||||
extern struct console early_hsu_console;
|
||||
extern void hsu_early_console_init(const char *);
|
||||
|
||||
extern void intel_scu_devices_create(void);
|
||||
extern void intel_scu_devices_destroy(void);
|
||||
|
||||
|
@ -375,12 +375,6 @@ static int __init setup_early_printk(char *buf)
|
||||
if (!strncmp(buf, "xen", 3))
|
||||
early_console_register(&xenboot_console, keep);
|
||||
#endif
|
||||
#ifdef CONFIG_EARLY_PRINTK_INTEL_MID
|
||||
if (!strncmp(buf, "hsu", 3)) {
|
||||
hsu_early_console_init(buf + 3);
|
||||
early_console_register(&early_hsu_console, keep);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_EARLY_PRINTK_EFI
|
||||
if (!strncmp(buf, "efi", 3))
|
||||
early_console_register(&early_efi_console, keep);
|
||||
|
@ -1,5 +1,4 @@
|
||||
obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o intel_mid_vrtc.o mfld.o mrfl.o
|
||||
obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o
|
||||
|
||||
# SFI specific code
|
||||
ifdef CONFIG_X86_INTEL_MID
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* early_printk_intel_mid.c - early consoles for Intel MID platforms
|
||||
*
|
||||
* Copyright (c) 2008-2010, Intel Corporation
|
||||
*
|
||||
* 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 file implements early console named hsu.
|
||||
* hsu is based on a High Speed UART device which only exists in the Medfield
|
||||
* platform
|
||||
*/
|
||||
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/serial_mfd.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/intel-mid.h>
|
||||
|
||||
/*
|
||||
* Following is the early console based on Medfield HSU (High
|
||||
* Speed UART) device.
|
||||
*/
|
||||
#define HSU_PORT_BASE 0xffa28080
|
||||
|
||||
static void __iomem *phsu;
|
||||
|
||||
void hsu_early_console_init(const char *s)
|
||||
{
|
||||
unsigned long paddr, port = 0;
|
||||
u8 lcr;
|
||||
|
||||
/*
|
||||
* Select the early HSU console port if specified by user in the
|
||||
* kernel command line.
|
||||
*/
|
||||
if (*s && !kstrtoul(s, 10, &port))
|
||||
port = clamp_val(port, 0, 2);
|
||||
|
||||
paddr = HSU_PORT_BASE + port * 0x80;
|
||||
phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
|
||||
|
||||
/* Disable FIFO */
|
||||
writeb(0x0, phsu + UART_FCR);
|
||||
|
||||
/* Set to default 115200 bps, 8n1 */
|
||||
lcr = readb(phsu + UART_LCR);
|
||||
writeb((0x80 | lcr), phsu + UART_LCR);
|
||||
writeb(0x18, phsu + UART_DLL);
|
||||
writeb(lcr, phsu + UART_LCR);
|
||||
writel(0x3600, phsu + UART_MUL*4);
|
||||
|
||||
writeb(0x8, phsu + UART_MCR);
|
||||
writeb(0x7, phsu + UART_FCR);
|
||||
writeb(0x3, phsu + UART_LCR);
|
||||
|
||||
/* Clear IRQ status */
|
||||
readb(phsu + UART_LSR);
|
||||
readb(phsu + UART_RX);
|
||||
readb(phsu + UART_IIR);
|
||||
readb(phsu + UART_MSR);
|
||||
|
||||
/* Enable FIFO */
|
||||
writeb(0x7, phsu + UART_FCR);
|
||||
}
|
||||
|
||||
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
||||
|
||||
static void early_hsu_putc(char ch)
|
||||
{
|
||||
unsigned int timeout = 10000; /* 10ms */
|
||||
u8 status;
|
||||
|
||||
while (--timeout) {
|
||||
status = readb(phsu + UART_LSR);
|
||||
if (status & BOTH_EMPTY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
/* Only write the char when there was no timeout */
|
||||
if (timeout)
|
||||
writeb(ch, phsu + UART_TX);
|
||||
}
|
||||
|
||||
static void early_hsu_write(struct console *con, const char *str, unsigned n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n && *str; i++) {
|
||||
if (*str == '\n')
|
||||
early_hsu_putc('\r');
|
||||
early_hsu_putc(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
struct console early_hsu_console = {
|
||||
.name = "earlyhsu",
|
||||
.write = early_hsu_write,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
};
|
@ -125,6 +125,8 @@ config FSL_DMA
|
||||
EloPlus is on mpc85xx and mpc86xx and Pxxx parts, and the Elo3 is on
|
||||
some Txxx and Bxxx parts.
|
||||
|
||||
source "drivers/dma/hsu/Kconfig"
|
||||
|
||||
config MPC512X_DMA
|
||||
tristate "Freescale MPC512x built-in DMA engine support"
|
||||
depends on PPC_MPC512x || PPC_MPC831x
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_DMATEST) += dmatest.o
|
||||
obj-$(CONFIG_INTEL_IOATDMA) += ioat/
|
||||
obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o
|
||||
obj-$(CONFIG_FSL_DMA) += fsldma.o
|
||||
obj-$(CONFIG_HSU_DMA) += hsu/
|
||||
obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o
|
||||
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
|
||||
obj-$(CONFIG_MV_XOR) += mv_xor.o
|
||||
|
14
drivers/dma/hsu/Kconfig
Normal file
14
drivers/dma/hsu/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
# DMA engine configuration for hsu
|
||||
config HSU_DMA
|
||||
tristate "High Speed UART DMA support"
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
|
||||
config HSU_DMA_PCI
|
||||
tristate "High Speed UART DMA PCI driver"
|
||||
depends on PCI
|
||||
select HSU_DMA
|
||||
help
|
||||
Support the High Speed UART DMA on the platfroms that
|
||||
enumerate it as a PCI device. For example, Intel Medfield
|
||||
has integrated this HSU DMA controller.
|
5
drivers/dma/hsu/Makefile
Normal file
5
drivers/dma/hsu/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
obj-$(CONFIG_HSU_DMA) += hsu_dma.o
|
||||
hsu_dma-objs := hsu.o
|
||||
|
||||
obj-$(CONFIG_HSU_DMA_PCI) += hsu_dma_pci.o
|
||||
hsu_dma_pci-objs := pci.o
|
504
drivers/dma/hsu/hsu.c
Normal file
504
drivers/dma/hsu/hsu.c
Normal file
@ -0,0 +1,504 @@
|
||||
/*
|
||||
* Core driver for the High Speed UART DMA
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*
|
||||
* Partially based on the bits found in drivers/tty/serial/mfd.c.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DMA channel allocation:
|
||||
* 1. Even number chans are used for DMA Read (UART TX), odd chans for DMA
|
||||
* Write (UART RX).
|
||||
* 2. 0/1 channel are assigned to port 0, 2/3 chan to port 1, 4/5 chan to
|
||||
* port 3, and so on.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "hsu.h"
|
||||
|
||||
#define HSU_DMA_BUSWIDTHS \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_16_BYTES)
|
||||
|
||||
static inline void hsu_chan_disable(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
hsu_chan_writel(hsuc, HSU_CH_CR, 0);
|
||||
}
|
||||
|
||||
static inline void hsu_chan_enable(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
u32 cr = HSU_CH_CR_CHA;
|
||||
|
||||
if (hsuc->direction == DMA_MEM_TO_DEV)
|
||||
cr &= ~HSU_CH_CR_CHD;
|
||||
else if (hsuc->direction == DMA_DEV_TO_MEM)
|
||||
cr |= HSU_CH_CR_CHD;
|
||||
|
||||
hsu_chan_writel(hsuc, HSU_CH_CR, cr);
|
||||
}
|
||||
|
||||
static void hsu_dma_chan_start(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
struct dma_slave_config *config = &hsuc->config;
|
||||
struct hsu_dma_desc *desc = hsuc->desc;
|
||||
u32 bsr, mtsr;
|
||||
u32 dcr = HSU_CH_DCR_CHSOE | HSU_CH_DCR_CHEI;
|
||||
unsigned int i, count;
|
||||
|
||||
if (hsuc->direction == DMA_MEM_TO_DEV) {
|
||||
bsr = config->dst_maxburst;
|
||||
mtsr = config->dst_addr_width;
|
||||
} else if (hsuc->direction == DMA_DEV_TO_MEM) {
|
||||
bsr = config->src_maxburst;
|
||||
mtsr = config->src_addr_width;
|
||||
} else {
|
||||
/* Not supported direction */
|
||||
return;
|
||||
}
|
||||
|
||||
hsu_chan_disable(hsuc);
|
||||
|
||||
hsu_chan_writel(hsuc, HSU_CH_DCR, 0);
|
||||
hsu_chan_writel(hsuc, HSU_CH_BSR, bsr);
|
||||
hsu_chan_writel(hsuc, HSU_CH_MTSR, mtsr);
|
||||
|
||||
/* Set descriptors */
|
||||
count = (desc->nents - desc->active) % HSU_DMA_CHAN_NR_DESC;
|
||||
for (i = 0; i < count; i++) {
|
||||
hsu_chan_writel(hsuc, HSU_CH_DxSAR(i), desc->sg[i].addr);
|
||||
hsu_chan_writel(hsuc, HSU_CH_DxTSR(i), desc->sg[i].len);
|
||||
|
||||
/* Prepare value for DCR */
|
||||
dcr |= HSU_CH_DCR_DESCA(i);
|
||||
dcr |= HSU_CH_DCR_CHTOI(i); /* timeout bit, see HSU Errata 1 */
|
||||
|
||||
desc->active++;
|
||||
}
|
||||
/* Only for the last descriptor in the chain */
|
||||
dcr |= HSU_CH_DCR_CHSOD(count - 1);
|
||||
dcr |= HSU_CH_DCR_CHDI(count - 1);
|
||||
|
||||
hsu_chan_writel(hsuc, HSU_CH_DCR, dcr);
|
||||
|
||||
hsu_chan_enable(hsuc);
|
||||
}
|
||||
|
||||
static void hsu_dma_stop_channel(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->lock, flags);
|
||||
hsu_chan_disable(hsuc);
|
||||
hsu_chan_writel(hsuc, HSU_CH_DCR, 0);
|
||||
spin_unlock_irqrestore(&hsuc->lock, flags);
|
||||
}
|
||||
|
||||
static void hsu_dma_start_channel(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->lock, flags);
|
||||
hsu_dma_chan_start(hsuc);
|
||||
spin_unlock_irqrestore(&hsuc->lock, flags);
|
||||
}
|
||||
|
||||
static void hsu_dma_start_transfer(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
struct virt_dma_desc *vdesc;
|
||||
|
||||
/* Get the next descriptor */
|
||||
vdesc = vchan_next_desc(&hsuc->vchan);
|
||||
if (!vdesc) {
|
||||
hsuc->desc = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
list_del(&vdesc->node);
|
||||
hsuc->desc = to_hsu_dma_desc(vdesc);
|
||||
|
||||
/* Start the channel with a new descriptor */
|
||||
hsu_dma_start_channel(hsuc);
|
||||
}
|
||||
|
||||
static u32 hsu_dma_chan_get_sr(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 sr;
|
||||
|
||||
spin_lock_irqsave(&hsuc->lock, flags);
|
||||
sr = hsu_chan_readl(hsuc, HSU_CH_SR);
|
||||
spin_unlock_irqrestore(&hsuc->lock, flags);
|
||||
|
||||
return sr;
|
||||
}
|
||||
|
||||
irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc;
|
||||
struct hsu_dma_desc *desc;
|
||||
unsigned long flags;
|
||||
u32 sr;
|
||||
|
||||
/* Sanity check */
|
||||
if (nr >= chip->pdata->nr_channels)
|
||||
return IRQ_NONE;
|
||||
|
||||
hsuc = &chip->hsu->chan[nr];
|
||||
|
||||
/*
|
||||
* No matter what situation, need read clear the IRQ status
|
||||
* There is a bug, see Errata 5, HSD 2900918
|
||||
*/
|
||||
sr = hsu_dma_chan_get_sr(hsuc);
|
||||
if (!sr)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Timeout IRQ, need wait some time, see Errata 2 */
|
||||
if (hsuc->direction == DMA_DEV_TO_MEM && (sr & HSU_CH_SR_DESCTO_ANY))
|
||||
udelay(2);
|
||||
|
||||
sr &= ~HSU_CH_SR_DESCTO_ANY;
|
||||
if (!sr)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
||||
desc = hsuc->desc;
|
||||
if (desc) {
|
||||
if (sr & HSU_CH_SR_CHE) {
|
||||
desc->status = DMA_ERROR;
|
||||
} else if (desc->active < desc->nents) {
|
||||
hsu_dma_start_channel(hsuc);
|
||||
} else {
|
||||
vchan_cookie_complete(&desc->vdesc);
|
||||
desc->status = DMA_COMPLETE;
|
||||
hsu_dma_start_transfer(hsuc);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hsu_dma_irq);
|
||||
|
||||
static struct hsu_dma_desc *hsu_dma_alloc_desc(unsigned int nents)
|
||||
{
|
||||
struct hsu_dma_desc *desc;
|
||||
|
||||
desc = kzalloc(sizeof(*desc), GFP_ATOMIC);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
desc->sg = kcalloc(nents, sizeof(*desc->sg), GFP_ATOMIC);
|
||||
if (!desc->sg) {
|
||||
kfree(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void hsu_dma_desc_free(struct virt_dma_desc *vdesc)
|
||||
{
|
||||
struct hsu_dma_desc *desc = to_hsu_dma_desc(vdesc);
|
||||
|
||||
kfree(desc->sg);
|
||||
kfree(desc);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *hsu_dma_prep_slave_sg(
|
||||
struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned int sg_len, enum dma_transfer_direction direction,
|
||||
unsigned long flags, void *context)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
struct hsu_dma_desc *desc;
|
||||
struct scatterlist *sg;
|
||||
unsigned int i;
|
||||
|
||||
desc = hsu_dma_alloc_desc(sg_len);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
desc->sg[i].addr = sg_dma_address(sg);
|
||||
desc->sg[i].len = sg_dma_len(sg);
|
||||
}
|
||||
|
||||
desc->nents = sg_len;
|
||||
desc->direction = direction;
|
||||
desc->active = 0;
|
||||
desc->status = DMA_IN_PROGRESS;
|
||||
|
||||
return vchan_tx_prep(&hsuc->vchan, &desc->vdesc, flags);
|
||||
}
|
||||
|
||||
static void hsu_dma_issue_pending(struct dma_chan *chan)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
||||
if (vchan_issue_pending(&hsuc->vchan) && !hsuc->desc)
|
||||
hsu_dma_start_transfer(hsuc);
|
||||
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
||||
}
|
||||
|
||||
static size_t hsu_dma_desc_size(struct hsu_dma_desc *desc)
|
||||
{
|
||||
size_t bytes = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = desc->active; i < desc->nents; i++)
|
||||
bytes += desc->sg[i].len;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static size_t hsu_dma_active_desc_size(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
struct hsu_dma_desc *desc = hsuc->desc;
|
||||
size_t bytes = hsu_dma_desc_size(desc);
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->lock, flags);
|
||||
i = desc->active % HSU_DMA_CHAN_NR_DESC;
|
||||
do {
|
||||
bytes += hsu_chan_readl(hsuc, HSU_CH_DxTSR(i));
|
||||
} while (--i >= 0);
|
||||
spin_unlock_irqrestore(&hsuc->lock, flags);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static enum dma_status hsu_dma_tx_status(struct dma_chan *chan,
|
||||
dma_cookie_t cookie, struct dma_tx_state *state)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
struct virt_dma_desc *vdesc;
|
||||
enum dma_status status;
|
||||
size_t bytes;
|
||||
unsigned long flags;
|
||||
|
||||
status = dma_cookie_status(chan, cookie, state);
|
||||
if (status == DMA_COMPLETE)
|
||||
return status;
|
||||
|
||||
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
||||
vdesc = vchan_find_desc(&hsuc->vchan, cookie);
|
||||
if (hsuc->desc && cookie == hsuc->desc->vdesc.tx.cookie) {
|
||||
bytes = hsu_dma_active_desc_size(hsuc);
|
||||
dma_set_residue(state, bytes);
|
||||
status = hsuc->desc->status;
|
||||
} else if (vdesc) {
|
||||
bytes = hsu_dma_desc_size(to_hsu_dma_desc(vdesc));
|
||||
dma_set_residue(state, bytes);
|
||||
}
|
||||
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hsu_dma_slave_config(struct dma_chan *chan,
|
||||
struct dma_slave_config *config)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
|
||||
/* Check if chan will be configured for slave transfers */
|
||||
if (!is_slave_direction(config->direction))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&hsuc->config, config, sizeof(hsuc->config));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hsu_dma_chan_deactivate(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->lock, flags);
|
||||
hsu_chan_disable(hsuc);
|
||||
spin_unlock_irqrestore(&hsuc->lock, flags);
|
||||
}
|
||||
|
||||
static void hsu_dma_chan_activate(struct hsu_dma_chan *hsuc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->lock, flags);
|
||||
hsu_chan_enable(hsuc);
|
||||
spin_unlock_irqrestore(&hsuc->lock, flags);
|
||||
}
|
||||
|
||||
static int hsu_dma_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
||||
if (hsuc->desc && hsuc->desc->status == DMA_IN_PROGRESS) {
|
||||
hsu_dma_chan_deactivate(hsuc);
|
||||
hsuc->desc->status = DMA_PAUSED;
|
||||
}
|
||||
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hsu_dma_resume(struct dma_chan *chan)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
||||
if (hsuc->desc && hsuc->desc->status == DMA_PAUSED) {
|
||||
hsuc->desc->status = DMA_IN_PROGRESS;
|
||||
hsu_dma_chan_activate(hsuc);
|
||||
}
|
||||
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hsu_dma_terminate_all(struct dma_chan *chan)
|
||||
{
|
||||
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
|
||||
unsigned long flags;
|
||||
LIST_HEAD(head);
|
||||
|
||||
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
||||
|
||||
hsu_dma_stop_channel(hsuc);
|
||||
hsuc->desc = NULL;
|
||||
|
||||
vchan_get_all_descriptors(&hsuc->vchan, &head);
|
||||
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
||||
vchan_dma_desc_free_list(&hsuc->vchan, &head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hsu_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hsu_dma_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
vchan_free_chan_resources(to_virt_chan(chan));
|
||||
}
|
||||
|
||||
int hsu_dma_probe(struct hsu_dma_chip *chip)
|
||||
{
|
||||
struct hsu_dma *hsu;
|
||||
struct hsu_dma_platform_data *pdata = chip->pdata;
|
||||
void __iomem *addr = chip->regs + chip->offset;
|
||||
unsigned short i;
|
||||
int ret;
|
||||
|
||||
hsu = devm_kzalloc(chip->dev, sizeof(*hsu), GFP_KERNEL);
|
||||
if (!hsu)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->hsu = hsu;
|
||||
|
||||
if (!pdata) {
|
||||
pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->pdata = pdata;
|
||||
|
||||
/* Guess nr_channels from the IO space length */
|
||||
pdata->nr_channels = (chip->length - chip->offset) /
|
||||
HSU_DMA_CHAN_LENGTH;
|
||||
}
|
||||
|
||||
hsu->chan = devm_kcalloc(chip->dev, pdata->nr_channels,
|
||||
sizeof(*hsu->chan), GFP_KERNEL);
|
||||
if (!hsu->chan)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&hsu->dma.channels);
|
||||
for (i = 0; i < pdata->nr_channels; i++) {
|
||||
struct hsu_dma_chan *hsuc = &hsu->chan[i];
|
||||
|
||||
hsuc->vchan.desc_free = hsu_dma_desc_free;
|
||||
vchan_init(&hsuc->vchan, &hsu->dma);
|
||||
|
||||
hsuc->direction = (i & 0x1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
|
||||
hsuc->reg = addr + i * HSU_DMA_CHAN_LENGTH;
|
||||
|
||||
spin_lock_init(&hsuc->lock);
|
||||
}
|
||||
|
||||
dma_cap_set(DMA_SLAVE, hsu->dma.cap_mask);
|
||||
dma_cap_set(DMA_PRIVATE, hsu->dma.cap_mask);
|
||||
|
||||
hsu->dma.device_alloc_chan_resources = hsu_dma_alloc_chan_resources;
|
||||
hsu->dma.device_free_chan_resources = hsu_dma_free_chan_resources;
|
||||
|
||||
hsu->dma.device_prep_slave_sg = hsu_dma_prep_slave_sg;
|
||||
|
||||
hsu->dma.device_issue_pending = hsu_dma_issue_pending;
|
||||
hsu->dma.device_tx_status = hsu_dma_tx_status;
|
||||
|
||||
hsu->dma.device_config = hsu_dma_slave_config;
|
||||
hsu->dma.device_pause = hsu_dma_pause;
|
||||
hsu->dma.device_resume = hsu_dma_resume;
|
||||
hsu->dma.device_terminate_all = hsu_dma_terminate_all;
|
||||
|
||||
hsu->dma.src_addr_widths = HSU_DMA_BUSWIDTHS;
|
||||
hsu->dma.dst_addr_widths = HSU_DMA_BUSWIDTHS;
|
||||
hsu->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
||||
hsu->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
|
||||
hsu->dma.dev = chip->dev;
|
||||
|
||||
ret = dma_async_device_register(&hsu->dma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(chip->dev, "Found HSU DMA, %d channels\n", pdata->nr_channels);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hsu_dma_probe);
|
||||
|
||||
int hsu_dma_remove(struct hsu_dma_chip *chip)
|
||||
{
|
||||
struct hsu_dma *hsu = chip->hsu;
|
||||
unsigned short i;
|
||||
|
||||
dma_async_device_unregister(&hsu->dma);
|
||||
|
||||
for (i = 0; i < chip->pdata->nr_channels; i++) {
|
||||
struct hsu_dma_chan *hsuc = &hsu->chan[i];
|
||||
|
||||
tasklet_kill(&hsuc->vchan.task);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hsu_dma_remove);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("High Speed UART DMA core driver");
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
118
drivers/dma/hsu/hsu.h
Normal file
118
drivers/dma/hsu/hsu.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Driver for the High Speed UART DMA
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*
|
||||
* Partially based on the bits found in drivers/tty/serial/mfd.c.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __DMA_HSU_H__
|
||||
#define __DMA_HSU_H__
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/dma/hsu.h>
|
||||
|
||||
#include "../virt-dma.h"
|
||||
|
||||
#define HSU_CH_SR 0x00 /* channel status */
|
||||
#define HSU_CH_CR 0x04 /* channel control */
|
||||
#define HSU_CH_DCR 0x08 /* descriptor control */
|
||||
#define HSU_CH_BSR 0x10 /* FIFO buffer size */
|
||||
#define HSU_CH_MTSR 0x14 /* minimum transfer size */
|
||||
#define HSU_CH_DxSAR(x) (0x20 + 8 * (x)) /* desc start addr */
|
||||
#define HSU_CH_DxTSR(x) (0x24 + 8 * (x)) /* desc transfer size */
|
||||
#define HSU_CH_D0SAR 0x20 /* desc 0 start addr */
|
||||
#define HSU_CH_D0TSR 0x24 /* desc 0 transfer size */
|
||||
#define HSU_CH_D1SAR 0x28
|
||||
#define HSU_CH_D1TSR 0x2c
|
||||
#define HSU_CH_D2SAR 0x30
|
||||
#define HSU_CH_D2TSR 0x34
|
||||
#define HSU_CH_D3SAR 0x38
|
||||
#define HSU_CH_D3TSR 0x3c
|
||||
|
||||
#define HSU_DMA_CHAN_NR_DESC 4
|
||||
#define HSU_DMA_CHAN_LENGTH 0x40
|
||||
|
||||
/* Bits in HSU_CH_SR */
|
||||
#define HSU_CH_SR_DESCTO(x) BIT(8 + (x))
|
||||
#define HSU_CH_SR_DESCTO_ANY (BIT(11) | BIT(10) | BIT(9) | BIT(8))
|
||||
#define HSU_CH_SR_CHE BIT(15)
|
||||
|
||||
/* Bits in HSU_CH_CR */
|
||||
#define HSU_CH_CR_CHA BIT(0)
|
||||
#define HSU_CH_CR_CHD BIT(1)
|
||||
|
||||
/* Bits in HSU_CH_DCR */
|
||||
#define HSU_CH_DCR_DESCA(x) BIT(0 + (x))
|
||||
#define HSU_CH_DCR_CHSOD(x) BIT(8 + (x))
|
||||
#define HSU_CH_DCR_CHSOTO BIT(14)
|
||||
#define HSU_CH_DCR_CHSOE BIT(15)
|
||||
#define HSU_CH_DCR_CHDI(x) BIT(16 + (x))
|
||||
#define HSU_CH_DCR_CHEI BIT(23)
|
||||
#define HSU_CH_DCR_CHTOI(x) BIT(24 + (x))
|
||||
|
||||
struct hsu_dma_sg {
|
||||
dma_addr_t addr;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct hsu_dma_desc {
|
||||
struct virt_dma_desc vdesc;
|
||||
enum dma_transfer_direction direction;
|
||||
struct hsu_dma_sg *sg;
|
||||
unsigned int nents;
|
||||
unsigned int active;
|
||||
enum dma_status status;
|
||||
};
|
||||
|
||||
static inline struct hsu_dma_desc *to_hsu_dma_desc(struct virt_dma_desc *vdesc)
|
||||
{
|
||||
return container_of(vdesc, struct hsu_dma_desc, vdesc);
|
||||
}
|
||||
|
||||
struct hsu_dma_chan {
|
||||
struct virt_dma_chan vchan;
|
||||
|
||||
void __iomem *reg;
|
||||
spinlock_t lock;
|
||||
|
||||
/* hardware configuration */
|
||||
enum dma_transfer_direction direction;
|
||||
struct dma_slave_config config;
|
||||
|
||||
struct hsu_dma_desc *desc;
|
||||
};
|
||||
|
||||
static inline struct hsu_dma_chan *to_hsu_dma_chan(struct dma_chan *chan)
|
||||
{
|
||||
return container_of(chan, struct hsu_dma_chan, vchan.chan);
|
||||
}
|
||||
|
||||
static inline u32 hsu_chan_readl(struct hsu_dma_chan *hsuc, int offset)
|
||||
{
|
||||
return readl(hsuc->reg + offset);
|
||||
}
|
||||
|
||||
static inline void hsu_chan_writel(struct hsu_dma_chan *hsuc, int offset,
|
||||
u32 value)
|
||||
{
|
||||
writel(value, hsuc->reg + offset);
|
||||
}
|
||||
|
||||
struct hsu_dma {
|
||||
struct dma_device dma;
|
||||
|
||||
/* channels */
|
||||
struct hsu_dma_chan *chan;
|
||||
};
|
||||
|
||||
static inline struct hsu_dma *to_hsu_dma(struct dma_device *ddev)
|
||||
{
|
||||
return container_of(ddev, struct hsu_dma, dma);
|
||||
}
|
||||
|
||||
#endif /* __DMA_HSU_H__ */
|
123
drivers/dma/hsu/pci.c
Normal file
123
drivers/dma/hsu/pci.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* PCI driver for the High Speed UART DMA
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*
|
||||
* Partially based on the bits found in drivers/tty/serial/mfd.c.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "hsu.h"
|
||||
|
||||
#define HSU_PCI_DMASR 0x00
|
||||
#define HSU_PCI_DMAISR 0x04
|
||||
|
||||
#define HSU_PCI_CHAN_OFFSET 0x100
|
||||
|
||||
static irqreturn_t hsu_pci_irq(int irq, void *dev)
|
||||
{
|
||||
struct hsu_dma_chip *chip = dev;
|
||||
u32 dmaisr;
|
||||
unsigned short i;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
dmaisr = readl(chip->regs + HSU_PCI_DMAISR);
|
||||
for (i = 0; i < chip->pdata->nr_channels; i++) {
|
||||
if (dmaisr & 0x1)
|
||||
ret |= hsu_dma_irq(chip, i);
|
||||
dmaisr >>= 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hsu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct hsu_dma_chip *chip;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "I/O memory remapping failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dev = &pdev->dev;
|
||||
chip->regs = pcim_iomap_table(pdev)[0];
|
||||
chip->length = pci_resource_len(pdev, 0);
|
||||
chip->offset = HSU_PCI_CHAN_OFFSET;
|
||||
chip->irq = pdev->irq;
|
||||
|
||||
pci_enable_msi(pdev);
|
||||
|
||||
ret = hsu_dma_probe(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = request_irq(chip->irq, hsu_pci_irq, 0, "hsu_dma_pci", chip);
|
||||
if (ret)
|
||||
goto err_register_irq;
|
||||
|
||||
pci_set_drvdata(pdev, chip);
|
||||
|
||||
return 0;
|
||||
|
||||
err_register_irq:
|
||||
hsu_dma_remove(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hsu_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct hsu_dma_chip *chip = pci_get_drvdata(pdev);
|
||||
|
||||
free_irq(chip->irq, chip);
|
||||
hsu_dma_remove(chip);
|
||||
}
|
||||
|
||||
static const struct pci_device_id hsu_pci_id_table[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x081e), 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, hsu_pci_id_table);
|
||||
|
||||
static struct pci_driver hsu_pci_driver = {
|
||||
.name = "hsu_dma_pci",
|
||||
.id_table = hsu_pci_id_table,
|
||||
.probe = hsu_pci_probe,
|
||||
.remove = hsu_pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(hsu_pci_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("High Speed UART DMA PCI driver");
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
@ -895,7 +895,7 @@ static int broken_efr(struct uart_8250_port *up)
|
||||
/*
|
||||
* Exar ST16C2550 "A2" devices incorrectly detect as
|
||||
* having an EFR, and report an ID of 0x0201. See
|
||||
* http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html
|
||||
* http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html
|
||||
*/
|
||||
if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16)
|
||||
return 1;
|
||||
@ -1260,7 +1260,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
|
||||
serial_out(up, UART_LCR, save_lcr);
|
||||
|
||||
port->fifosize = uart_config[up->port.type].fifo_size;
|
||||
old_capabilities = up->capabilities;
|
||||
old_capabilities = up->capabilities;
|
||||
up->capabilities = uart_config[port->type].flags;
|
||||
up->tx_loadsz = uart_config[port->type].tx_loadsz;
|
||||
|
||||
|
@ -416,18 +416,24 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_8250_port uart = {};
|
||||
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
struct dw8250_data *data;
|
||||
int err;
|
||||
|
||||
if (!regs || !irq) {
|
||||
dev_err(&pdev->dev, "no registers/irq defined\n");
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "no registers defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq < 0) {
|
||||
if (irq != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "cannot get irq\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
spin_lock_init(&uart.port.lock);
|
||||
uart.port.mapbase = regs->start;
|
||||
uart.port.irq = irq->start;
|
||||
uart.port.irq = irq;
|
||||
uart.port.handle_irq = dw8250_handle_irq;
|
||||
uart.port.pm = dw8250_do_pm;
|
||||
uart.port.type = PORT_8250;
|
||||
@ -640,3 +646,4 @@ module_platform_driver(dw8250_platform_driver);
|
||||
MODULE_AUTHOR("Jamie Iles");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver");
|
||||
MODULE_ALIAS("platform:dw-apb-uart");
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/platform_data/dma-dw.h>
|
||||
#include <linux/platform_data/dma-hsu.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
@ -1525,6 +1526,148 @@ byt_serial_setup(struct serial_private *priv,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define INTEL_MID_UART_PS 0x30
|
||||
#define INTEL_MID_UART_MUL 0x34
|
||||
|
||||
static void intel_mid_set_termios_50M(struct uart_port *p,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud = tty_termios_baud_rate(termios);
|
||||
u32 ps, mul;
|
||||
|
||||
/*
|
||||
* The uart clk is 50Mhz, and the baud rate come from:
|
||||
* baud = 50M * MUL / (DIV * PS * DLAB)
|
||||
*
|
||||
* For those basic low baud rate we can get the direct
|
||||
* scalar from 2746800, like 115200 = 2746800/24. For those
|
||||
* higher baud rate, we handle them case by case, mainly by
|
||||
* adjusting the MUL/PS registers, and DIV register is kept
|
||||
* as default value 0x3d09 to make things simple.
|
||||
*/
|
||||
|
||||
ps = 0x10;
|
||||
|
||||
switch (baud) {
|
||||
case 500000:
|
||||
case 1000000:
|
||||
case 1500000:
|
||||
case 3000000:
|
||||
mul = 0x3a98;
|
||||
p->uartclk = 48000000;
|
||||
break;
|
||||
case 2000000:
|
||||
case 4000000:
|
||||
mul = 0x2710;
|
||||
ps = 0x08;
|
||||
p->uartclk = 64000000;
|
||||
break;
|
||||
case 2500000:
|
||||
mul = 0x30d4;
|
||||
p->uartclk = 40000000;
|
||||
break;
|
||||
case 3500000:
|
||||
mul = 0x3345;
|
||||
ps = 0x0c;
|
||||
p->uartclk = 56000000;
|
||||
break;
|
||||
default:
|
||||
mul = 0x2400;
|
||||
p->uartclk = 29491200;
|
||||
}
|
||||
|
||||
writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */
|
||||
writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */
|
||||
|
||||
serial8250_do_set_termios(p, termios, old);
|
||||
}
|
||||
|
||||
static bool intel_mid_dma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct hsu_dma_slave *s = param;
|
||||
|
||||
if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id)
|
||||
return false;
|
||||
|
||||
chan->private = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int intel_mid_serial_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx,
|
||||
int index, struct pci_dev *dma_dev)
|
||||
{
|
||||
struct device *dev = port->port.dev;
|
||||
struct uart_8250_dma *dma;
|
||||
struct hsu_dma_slave *tx_param, *rx_param;
|
||||
|
||||
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
||||
if (!dma)
|
||||
return -ENOMEM;
|
||||
|
||||
tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
|
||||
if (!tx_param)
|
||||
return -ENOMEM;
|
||||
|
||||
rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
|
||||
if (!rx_param)
|
||||
return -ENOMEM;
|
||||
|
||||
rx_param->chan_id = index * 2 + 1;
|
||||
tx_param->chan_id = index * 2;
|
||||
|
||||
dma->rxconf.src_maxburst = 64;
|
||||
dma->txconf.dst_maxburst = 64;
|
||||
|
||||
rx_param->dma_dev = &dma_dev->dev;
|
||||
tx_param->dma_dev = &dma_dev->dev;
|
||||
|
||||
dma->fn = intel_mid_dma_filter;
|
||||
dma->rx_param = rx_param;
|
||||
dma->tx_param = tx_param;
|
||||
|
||||
port->port.type = PORT_16750;
|
||||
port->port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
|
||||
port->dma = dma;
|
||||
|
||||
return pci_default_setup(priv, board, port, idx);
|
||||
}
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b
|
||||
#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c
|
||||
#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d
|
||||
|
||||
static int pnw_serial_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx)
|
||||
{
|
||||
struct pci_dev *pdev = priv->dev;
|
||||
struct pci_dev *dma_dev;
|
||||
int index;
|
||||
|
||||
switch (pdev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_PNW_UART1:
|
||||
index = 0;
|
||||
break;
|
||||
case PCI_DEVICE_ID_INTEL_PNW_UART2:
|
||||
index = 1;
|
||||
break;
|
||||
case PCI_DEVICE_ID_INTEL_PNW_UART3:
|
||||
index = 2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 3));
|
||||
|
||||
port->port.set_termios = intel_mid_set_termios_50M;
|
||||
|
||||
return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev);
|
||||
}
|
||||
|
||||
static int
|
||||
pci_omegapci_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
@ -1987,6 +2130,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = byt_serial_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_PNW_UART1,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pnw_serial_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_PNW_UART2,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pnw_serial_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_PNW_UART3,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pnw_serial_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_BSW_UART1,
|
||||
@ -2864,6 +3028,7 @@ enum pci_board_num_t {
|
||||
pbn_ADDIDATA_PCIe_8_3906250,
|
||||
pbn_ce4100_1_115200,
|
||||
pbn_byt,
|
||||
pbn_pnw,
|
||||
pbn_qrk,
|
||||
pbn_omegapci,
|
||||
pbn_NETMOS9900_2s_115200,
|
||||
@ -3630,6 +3795,11 @@ static struct pciserial_board pci_boards[] = {
|
||||
.uart_offset = 0x80,
|
||||
.reg_shift = 2,
|
||||
},
|
||||
[pbn_pnw] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
},
|
||||
[pbn_qrk] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 1,
|
||||
@ -4006,41 +4176,41 @@ static void pciserial_remove_one(struct pci_dev *dev)
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pciserial_suspend_one(struct device *dev)
|
||||
{
|
||||
struct serial_private *priv = pci_get_drvdata(dev);
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct serial_private *priv = pci_get_drvdata(pdev);
|
||||
|
||||
if (priv)
|
||||
pciserial_suspend_ports(priv);
|
||||
|
||||
pci_save_state(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pciserial_resume_one(struct pci_dev *dev)
|
||||
static int pciserial_resume_one(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct serial_private *priv = pci_get_drvdata(pdev);
|
||||
int err;
|
||||
struct serial_private *priv = pci_get_drvdata(dev);
|
||||
|
||||
pci_set_power_state(dev, PCI_D0);
|
||||
pci_restore_state(dev);
|
||||
|
||||
if (priv) {
|
||||
/*
|
||||
* The device may have been disabled. Re-enable it.
|
||||
*/
|
||||
err = pci_enable_device(dev);
|
||||
err = pci_enable_device(pdev);
|
||||
/* FIXME: We cannot simply error out here */
|
||||
if (err)
|
||||
dev_err(&dev->dev, "Unable to re-enable ports, trying to continue.\n");
|
||||
dev_err(dev, "Unable to re-enable ports, trying to continue.\n");
|
||||
pciserial_resume_ports(priv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pciserial_pm_ops, pciserial_suspend_one,
|
||||
pciserial_resume_one);
|
||||
|
||||
static struct pci_device_id serial_pci_tbl[] = {
|
||||
/* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */
|
||||
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620,
|
||||
@ -5362,6 +5532,19 @@ static struct pci_device_id serial_pci_tbl[] = {
|
||||
PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
|
||||
pbn_byt },
|
||||
|
||||
/*
|
||||
* Intel Penwell
|
||||
*/
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART1,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_pnw},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART2,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_pnw},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART3,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_pnw},
|
||||
|
||||
/*
|
||||
* Intel Quark x1000
|
||||
*/
|
||||
@ -5510,10 +5693,9 @@ static struct pci_driver serial_pci_driver = {
|
||||
.name = "serial",
|
||||
.probe = pciserial_init_one,
|
||||
.remove = pciserial_remove_one,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = pciserial_suspend_one,
|
||||
.resume = pciserial_resume_one,
|
||||
#endif
|
||||
.driver = {
|
||||
.pm = &pciserial_pm_ops,
|
||||
},
|
||||
.id_table = serial_pci_tbl,
|
||||
.err_handler = &serial8250_err_handler,
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ comment "Non-8250 serial port support"
|
||||
|
||||
config SERIAL_AMBA_PL010
|
||||
tristate "ARM AMBA PL010 serial port support"
|
||||
depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE)
|
||||
depends on ARM_AMBA
|
||||
select SERIAL_CORE
|
||||
help
|
||||
This selects the ARM(R) AMBA(R) PrimeCell PL010 UART. If you have
|
||||
@ -483,16 +483,6 @@ config SERIAL_SA1100_CONSOLE
|
||||
your boot loader (lilo or loadlin) about how to pass options to the
|
||||
kernel at boot time.)
|
||||
|
||||
config SERIAL_MFD_HSU
|
||||
tristate "Medfield High Speed UART support"
|
||||
depends on PCI
|
||||
select SERIAL_CORE
|
||||
|
||||
config SERIAL_MFD_HSU_CONSOLE
|
||||
bool "Medfile HSU serial console support"
|
||||
depends on SERIAL_MFD_HSU=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
|
||||
config SERIAL_BFIN
|
||||
tristate "Blackfin serial port support"
|
||||
depends on BLACKFIN
|
||||
@ -835,7 +825,7 @@ config SERIAL_MCF_CONSOLE
|
||||
|
||||
config SERIAL_PMACZILOG
|
||||
tristate "Mac or PowerMac z85c30 ESCC support"
|
||||
depends on (M68K && MAC) || (PPC_OF && PPC_PMAC)
|
||||
depends on (M68K && MAC) || PPC_PMAC
|
||||
select SERIAL_CORE
|
||||
help
|
||||
This driver supports the Zilog z85C30 serial ports found on
|
||||
@ -1153,7 +1143,7 @@ config SERIAL_OMAP_CONSOLE
|
||||
|
||||
config SERIAL_OF_PLATFORM_NWPSERIAL
|
||||
tristate "NWP serial port driver"
|
||||
depends on PPC_OF && PPC_DCR
|
||||
depends on PPC_DCR
|
||||
select SERIAL_OF_PLATFORM
|
||||
select SERIAL_CORE_CONSOLE
|
||||
select SERIAL_CORE
|
||||
|
@ -78,7 +78,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
|
||||
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
|
||||
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
|
||||
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
|
||||
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
|
||||
obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
|
||||
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
|
||||
obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define UART_NR 14
|
||||
|
||||
@ -156,7 +157,9 @@ struct uart_amba_port {
|
||||
unsigned int lcrh_tx; /* vendor-specific */
|
||||
unsigned int lcrh_rx; /* vendor-specific */
|
||||
unsigned int old_cr; /* state during shutdown */
|
||||
struct delayed_work tx_softirq_work;
|
||||
bool autorts;
|
||||
unsigned int tx_irq_seen; /* 0=none, 1=1, 2=2 or more */
|
||||
char type[12];
|
||||
#ifdef CONFIG_DMA_ENGINE
|
||||
/* DMA stuff */
|
||||
@ -164,6 +167,7 @@ struct uart_amba_port {
|
||||
bool using_rx_dma;
|
||||
struct pl011_dmarx_data dmarx;
|
||||
struct pl011_dmatx_data dmatx;
|
||||
bool dma_probed;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -261,10 +265,11 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
|
||||
}
|
||||
}
|
||||
|
||||
static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
|
||||
static void pl011_dma_probe(struct uart_amba_port *uap)
|
||||
{
|
||||
/* DMA is the sole user of the platform data right now */
|
||||
struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
|
||||
struct device *dev = uap->port.dev;
|
||||
struct dma_slave_config tx_conf = {
|
||||
.dst_addr = uap->port.mapbase + UART01x_DR,
|
||||
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
|
||||
@ -275,9 +280,15 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
|
||||
struct dma_chan *chan;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
chan = dma_request_slave_channel(dev, "tx");
|
||||
uap->dma_probed = true;
|
||||
chan = dma_request_slave_channel_reason(dev, "tx");
|
||||
if (IS_ERR(chan)) {
|
||||
if (PTR_ERR(chan) == -EPROBE_DEFER) {
|
||||
dev_info(uap->port.dev, "DMA driver not ready\n");
|
||||
uap->dma_probed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chan) {
|
||||
/* We need platform data */
|
||||
if (!plat || !plat->dma_filter) {
|
||||
dev_info(uap->port.dev, "no DMA platform data\n");
|
||||
@ -385,63 +396,17 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
/*
|
||||
* Stack up the UARTs and let the above initcall be done at device
|
||||
* initcall time, because the serial driver is called as an arch
|
||||
* initcall, and at this time the DMA subsystem is not yet registered.
|
||||
* At this point the driver will switch over to using DMA where desired.
|
||||
*/
|
||||
struct dma_uap {
|
||||
struct list_head node;
|
||||
struct uart_amba_port *uap;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static LIST_HEAD(pl011_dma_uarts);
|
||||
|
||||
static int __init pl011_dma_initcall(void)
|
||||
{
|
||||
struct list_head *node, *tmp;
|
||||
|
||||
list_for_each_safe(node, tmp, &pl011_dma_uarts) {
|
||||
struct dma_uap *dmau = list_entry(node, struct dma_uap, node);
|
||||
pl011_dma_probe_initcall(dmau->dev, dmau->uap);
|
||||
list_del(node);
|
||||
kfree(dmau);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(pl011_dma_initcall);
|
||||
|
||||
static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
|
||||
{
|
||||
struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL);
|
||||
if (dmau) {
|
||||
dmau->uap = uap;
|
||||
dmau->dev = dev;
|
||||
list_add_tail(&dmau->node, &pl011_dma_uarts);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
|
||||
{
|
||||
pl011_dma_probe_initcall(dev, uap);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pl011_dma_remove(struct uart_amba_port *uap)
|
||||
{
|
||||
/* TODO: remove the initcall if it has not yet executed */
|
||||
if (uap->dmatx.chan)
|
||||
dma_release_channel(uap->dmatx.chan);
|
||||
if (uap->dmarx.chan)
|
||||
dma_release_channel(uap->dmarx.chan);
|
||||
}
|
||||
|
||||
/* Forward declare this for the refill routine */
|
||||
/* Forward declare these for the refill routine */
|
||||
static int pl011_dma_tx_refill(struct uart_amba_port *uap);
|
||||
static void pl011_start_tx_pio(struct uart_amba_port *uap);
|
||||
|
||||
/*
|
||||
* The current DMA TX buffer has been sent.
|
||||
@ -479,14 +444,13 @@ static void pl011_dma_tx_callback(void *data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (pl011_dma_tx_refill(uap) <= 0) {
|
||||
if (pl011_dma_tx_refill(uap) <= 0)
|
||||
/*
|
||||
* We didn't queue a DMA buffer for some reason, but we
|
||||
* have data pending to be sent. Re-enable the TX IRQ.
|
||||
*/
|
||||
uap->im |= UART011_TXIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
}
|
||||
pl011_start_tx_pio(uap);
|
||||
|
||||
spin_unlock_irqrestore(&uap->port.lock, flags);
|
||||
}
|
||||
|
||||
@ -664,12 +628,10 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
|
||||
if (!uap->dmatx.queued) {
|
||||
if (pl011_dma_tx_refill(uap) > 0) {
|
||||
uap->im &= ~UART011_TXIM;
|
||||
ret = true;
|
||||
} else {
|
||||
uap->im |= UART011_TXIM;
|
||||
writew(uap->im, uap->port.membase +
|
||||
UART011_IMSC);
|
||||
} else
|
||||
ret = false;
|
||||
}
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
} else if (!(uap->dmacr & UART011_TXDMAE)) {
|
||||
uap->dmacr |= UART011_TXDMAE;
|
||||
writew(uap->dmacr,
|
||||
@ -1021,6 +983,9 @@ static void pl011_dma_startup(struct uart_amba_port *uap)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!uap->dma_probed)
|
||||
pl011_dma_probe(uap);
|
||||
|
||||
if (!uap->dmatx.chan)
|
||||
return;
|
||||
|
||||
@ -1142,7 +1107,7 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
|
||||
|
||||
#else
|
||||
/* Blank functions if the DMA engine is not available */
|
||||
static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
|
||||
static inline void pl011_dma_probe(struct uart_amba_port *uap)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1208,15 +1173,24 @@ static void pl011_stop_tx(struct uart_port *port)
|
||||
pl011_dma_tx_stop(uap);
|
||||
}
|
||||
|
||||
static bool pl011_tx_chars(struct uart_amba_port *uap);
|
||||
|
||||
/* Start TX with programmed I/O only (no DMA) */
|
||||
static void pl011_start_tx_pio(struct uart_amba_port *uap)
|
||||
{
|
||||
uap->im |= UART011_TXIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
if (!uap->tx_irq_seen)
|
||||
pl011_tx_chars(uap);
|
||||
}
|
||||
|
||||
static void pl011_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap =
|
||||
container_of(port, struct uart_amba_port, port);
|
||||
|
||||
if (!pl011_dma_tx_start(uap)) {
|
||||
uap->im |= UART011_TXIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
}
|
||||
if (!pl011_dma_tx_start(uap))
|
||||
pl011_start_tx_pio(uap);
|
||||
}
|
||||
|
||||
static void pl011_stop_rx(struct uart_port *port)
|
||||
@ -1274,40 +1248,87 @@ __acquires(&uap->port.lock)
|
||||
spin_lock(&uap->port.lock);
|
||||
}
|
||||
|
||||
static void pl011_tx_chars(struct uart_amba_port *uap)
|
||||
/*
|
||||
* Transmit a character
|
||||
* There must be at least one free entry in the TX FIFO to accept the char.
|
||||
*
|
||||
* Returns true if the FIFO might have space in it afterwards;
|
||||
* returns false if the FIFO definitely became full.
|
||||
*/
|
||||
static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c)
|
||||
{
|
||||
writew(c, uap->port.membase + UART01x_DR);
|
||||
uap->port.icount.tx++;
|
||||
|
||||
if (likely(uap->tx_irq_seen > 1))
|
||||
return true;
|
||||
|
||||
return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF);
|
||||
}
|
||||
|
||||
static bool pl011_tx_chars(struct uart_amba_port *uap)
|
||||
{
|
||||
struct circ_buf *xmit = &uap->port.state->xmit;
|
||||
int count;
|
||||
|
||||
if (unlikely(uap->tx_irq_seen < 2))
|
||||
/*
|
||||
* Initial FIFO fill level unknown: we must check TXFF
|
||||
* after each write, so just try to fill up the FIFO.
|
||||
*/
|
||||
count = uap->fifosize;
|
||||
else /* tx_irq_seen >= 2 */
|
||||
/*
|
||||
* FIFO initially at least half-empty, so we can simply
|
||||
* write half the FIFO without polling TXFF.
|
||||
|
||||
* Note: the *first* TX IRQ can still race with
|
||||
* pl011_start_tx_pio(), which can result in the FIFO
|
||||
* being fuller than expected in that case.
|
||||
*/
|
||||
count = uap->fifosize >> 1;
|
||||
|
||||
/*
|
||||
* If the FIFO is full we're guaranteed a TX IRQ at some later point,
|
||||
* and can't transmit immediately in any case:
|
||||
*/
|
||||
if (unlikely(uap->tx_irq_seen < 2 &&
|
||||
readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF))
|
||||
return false;
|
||||
|
||||
if (uap->port.x_char) {
|
||||
writew(uap->port.x_char, uap->port.membase + UART01x_DR);
|
||||
uap->port.icount.tx++;
|
||||
pl011_tx_char(uap, uap->port.x_char);
|
||||
uap->port.x_char = 0;
|
||||
return;
|
||||
--count;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
|
||||
pl011_stop_tx(&uap->port);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If we are using DMA mode, try to send some characters. */
|
||||
if (pl011_dma_tx_irq(uap))
|
||||
return;
|
||||
goto done;
|
||||
|
||||
count = uap->fifosize >> 1;
|
||||
do {
|
||||
writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR);
|
||||
while (count-- > 0 && pl011_tx_char(uap, xmit->buf[xmit->tail])) {
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
uap->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0);
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&uap->port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
if (uart_circ_empty(xmit)) {
|
||||
pl011_stop_tx(&uap->port);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (unlikely(!uap->tx_irq_seen))
|
||||
schedule_delayed_work(&uap->tx_softirq_work, uap->port.timeout);
|
||||
|
||||
done:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pl011_modem_status(struct uart_amba_port *uap)
|
||||
@ -1334,6 +1355,28 @@ static void pl011_modem_status(struct uart_amba_port *uap)
|
||||
wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
|
||||
}
|
||||
|
||||
static void pl011_tx_softirq(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct uart_amba_port *uap =
|
||||
container_of(dwork, struct uart_amba_port, tx_softirq_work);
|
||||
|
||||
spin_lock(&uap->port.lock);
|
||||
while (pl011_tx_chars(uap)) ;
|
||||
spin_unlock(&uap->port.lock);
|
||||
}
|
||||
|
||||
static void pl011_tx_irq_seen(struct uart_amba_port *uap)
|
||||
{
|
||||
if (likely(uap->tx_irq_seen > 1))
|
||||
return;
|
||||
|
||||
uap->tx_irq_seen++;
|
||||
if (uap->tx_irq_seen < 2)
|
||||
/* first TX IRQ */
|
||||
cancel_delayed_work(&uap->tx_softirq_work);
|
||||
}
|
||||
|
||||
static irqreturn_t pl011_int(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_amba_port *uap = dev_id;
|
||||
@ -1372,8 +1415,10 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
|
||||
if (status & (UART011_DSRMIS|UART011_DCDMIS|
|
||||
UART011_CTSMIS|UART011_RIMIS))
|
||||
pl011_modem_status(uap);
|
||||
if (status & UART011_TXIS)
|
||||
if (status & UART011_TXIS) {
|
||||
pl011_tx_irq_seen(uap);
|
||||
pl011_tx_chars(uap);
|
||||
}
|
||||
|
||||
if (pass_counter-- == 0)
|
||||
break;
|
||||
@ -1577,7 +1622,7 @@ static int pl011_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap =
|
||||
container_of(port, struct uart_amba_port, port);
|
||||
unsigned int cr, lcr_h, fbrd, ibrd;
|
||||
unsigned int cr;
|
||||
int retval;
|
||||
|
||||
retval = pl011_hwinit(port);
|
||||
@ -1595,30 +1640,8 @@ static int pl011_startup(struct uart_port *port)
|
||||
|
||||
writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
|
||||
|
||||
/*
|
||||
* Provoke TX FIFO interrupt into asserting. Taking care to preserve
|
||||
* baud rate and data format specified by FBRD, IBRD and LCRH as the
|
||||
* UART may already be in use as a console.
|
||||
*/
|
||||
spin_lock_irq(&uap->port.lock);
|
||||
|
||||
fbrd = readw(uap->port.membase + UART011_FBRD);
|
||||
ibrd = readw(uap->port.membase + UART011_IBRD);
|
||||
lcr_h = readw(uap->port.membase + uap->lcrh_rx);
|
||||
|
||||
cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
|
||||
writew(cr, uap->port.membase + UART011_CR);
|
||||
writew(0, uap->port.membase + UART011_FBRD);
|
||||
writew(1, uap->port.membase + UART011_IBRD);
|
||||
pl011_write_lcr_h(uap, 0);
|
||||
writew(0, uap->port.membase + UART01x_DR);
|
||||
while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
|
||||
barrier();
|
||||
|
||||
writew(fbrd, uap->port.membase + UART011_FBRD);
|
||||
writew(ibrd, uap->port.membase + UART011_IBRD);
|
||||
pl011_write_lcr_h(uap, lcr_h);
|
||||
|
||||
/* restore RTS and DTR */
|
||||
cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
|
||||
cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
|
||||
@ -1672,13 +1695,15 @@ static void pl011_shutdown(struct uart_port *port)
|
||||
container_of(port, struct uart_amba_port, port);
|
||||
unsigned int cr;
|
||||
|
||||
cancel_delayed_work_sync(&uap->tx_softirq_work);
|
||||
|
||||
/*
|
||||
* disable all interrupts
|
||||
*/
|
||||
spin_lock_irq(&uap->port.lock);
|
||||
uap->im = 0;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
writew(0xffff, uap->port.membase + UART011_ICR);
|
||||
writew(0xffff & ~UART011_TXIS, uap->port.membase + UART011_ICR);
|
||||
spin_unlock_irq(&uap->port.lock);
|
||||
|
||||
pl011_dma_shutdown(uap);
|
||||
@ -2218,7 +2243,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
uap->port.ops = &amba_pl011_pops;
|
||||
uap->port.flags = UPF_BOOT_AUTOCONF;
|
||||
uap->port.line = i;
|
||||
pl011_dma_probe(&dev->dev, uap);
|
||||
INIT_DELAYED_WORK(&uap->tx_softirq_work, pl011_tx_softirq);
|
||||
|
||||
/* Ensure interrupts from this UART are masked and cleared */
|
||||
writew(0, uap->port.membase + UART011_IMSC);
|
||||
@ -2233,7 +2258,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
if (!amba_reg.state) {
|
||||
ret = uart_register_driver(&amba_reg);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to register AMBA-PL011 driver\n");
|
||||
dev_err(&dev->dev,
|
||||
"Failed to register AMBA-PL011 driver\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -2242,7 +2268,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
if (ret) {
|
||||
amba_ports[i] = NULL;
|
||||
uart_unregister_driver(&amba_reg);
|
||||
pl011_dma_remove(uap);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -649,7 +649,7 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
||||
id = 0;
|
||||
}
|
||||
|
||||
if (id > CONFIG_SERIAL_AR933X_NR_UARTS)
|
||||
if (id >= CONFIG_SERIAL_AR933X_NR_UARTS)
|
||||
return -EINVAL;
|
||||
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
|
@ -855,7 +855,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
|
||||
spin_lock_init(&atmel_port->lock_tx);
|
||||
sg_init_table(&atmel_port->sg_tx, 1);
|
||||
/* UART circular tx buffer is an aligned page. */
|
||||
BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
|
||||
BUG_ON(!PAGE_ALIGNED(port->state->xmit.buf));
|
||||
sg_set_page(&atmel_port->sg_tx,
|
||||
virt_to_page(port->state->xmit.buf),
|
||||
UART_XMIT_SIZE,
|
||||
@ -1034,10 +1034,10 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
|
||||
spin_lock_init(&atmel_port->lock_rx);
|
||||
sg_init_table(&atmel_port->sg_rx, 1);
|
||||
/* UART circular rx buffer is an aligned page. */
|
||||
BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
|
||||
BUG_ON(!PAGE_ALIGNED(ring->buf));
|
||||
sg_set_page(&atmel_port->sg_rx,
|
||||
virt_to_page(ring->buf),
|
||||
ATMEL_SERIAL_RINGSIZE,
|
||||
sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE,
|
||||
(int)ring->buf & ~PAGE_MASK);
|
||||
nent = dma_map_sg(port->dev,
|
||||
&atmel_port->sg_rx,
|
||||
@ -1554,7 +1554,7 @@ static void atmel_tasklet_func(unsigned long data)
|
||||
spin_unlock(&port->lock);
|
||||
}
|
||||
|
||||
static int atmel_init_property(struct atmel_uart_port *atmel_port,
|
||||
static void atmel_init_property(struct atmel_uart_port *atmel_port,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
@ -1595,7 +1595,6 @@ static int atmel_init_property(struct atmel_uart_port *atmel_port,
|
||||
atmel_port->use_dma_tx = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_init_rs485(struct uart_port *port,
|
||||
@ -1777,10 +1776,13 @@ static int atmel_startup(struct uart_port *port)
|
||||
if (retval)
|
||||
goto free_irq;
|
||||
|
||||
tasklet_enable(&atmel_port->tasklet);
|
||||
|
||||
/*
|
||||
* Initialize DMA (if necessary)
|
||||
*/
|
||||
atmel_init_property(atmel_port, pdev);
|
||||
atmel_set_ops(port);
|
||||
|
||||
if (atmel_port->prepare_rx) {
|
||||
retval = atmel_port->prepare_rx(port);
|
||||
@ -1879,6 +1881,7 @@ static void atmel_shutdown(struct uart_port *port)
|
||||
* Clear out any scheduled tasklets before
|
||||
* we destroy the buffers
|
||||
*/
|
||||
tasklet_disable(&atmel_port->tasklet);
|
||||
tasklet_kill(&atmel_port->tasklet);
|
||||
|
||||
/*
|
||||
@ -2256,8 +2259,8 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
|
||||
struct uart_port *port = &atmel_port->uart;
|
||||
struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!atmel_init_property(atmel_port, pdev))
|
||||
atmel_set_ops(port);
|
||||
atmel_init_property(atmel_port, pdev);
|
||||
atmel_set_ops(port);
|
||||
|
||||
atmel_init_rs485(port, pdev);
|
||||
|
||||
@ -2272,6 +2275,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
|
||||
|
||||
tasklet_init(&atmel_port->tasklet, atmel_tasklet_func,
|
||||
(unsigned long)port);
|
||||
tasklet_disable(&atmel_port->tasklet);
|
||||
|
||||
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
|
||||
|
||||
@ -2581,8 +2585,8 @@ static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev)
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
p->gpios = mctrl_gpio_init(dev, 0);
|
||||
if (IS_ERR_OR_NULL(p->gpios))
|
||||
return -1;
|
||||
if (IS_ERR(p->gpios))
|
||||
return PTR_ERR(p->gpios);
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
gpiod = mctrl_gpio_to_gpiod(p->gpios, i);
|
||||
@ -2635,9 +2639,10 @@ static int atmel_serial_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&port->lock_suspended);
|
||||
|
||||
ret = atmel_init_gpios(port, &pdev->dev);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "%s",
|
||||
"Failed to initialize GPIOs. The serial port may not work as expected");
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to initialize GPIOs.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = atmel_init_port(port, pdev);
|
||||
if (ret)
|
||||
|
@ -854,7 +854,7 @@ static int bcm_uart_probe(struct platform_device *pdev)
|
||||
|
||||
ret = uart_add_one_port(&bcm_uart_driver, port);
|
||||
if (ret) {
|
||||
ports[pdev->id].membase = 0;
|
||||
ports[pdev->id].membase = NULL;
|
||||
return ret;
|
||||
}
|
||||
platform_set_drvdata(pdev, port);
|
||||
@ -868,7 +868,7 @@ static int bcm_uart_remove(struct platform_device *pdev)
|
||||
port = platform_get_drvdata(pdev);
|
||||
uart_remove_one_port(&bcm_uart_driver, port);
|
||||
/* mark port as free */
|
||||
ports[pdev->id].membase = 0;
|
||||
ports[pdev->id].membase = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -501,6 +501,8 @@ static int uart_clps711x_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, s);
|
||||
|
||||
s->gpios = mctrl_gpio_init(&pdev->dev, 0);
|
||||
if (IS_ERR(s->gpios))
|
||||
return PTR_ERR(s->gpios);
|
||||
|
||||
ret = uart_add_one_port(&clps711x_uart, &s->port);
|
||||
if (ret)
|
||||
|
@ -54,44 +54,31 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
|
||||
return base;
|
||||
}
|
||||
|
||||
static int __init parse_options(struct earlycon_device *device,
|
||||
char *options)
|
||||
static int __init parse_options(struct earlycon_device *device, char *options)
|
||||
{
|
||||
struct uart_port *port = &device->port;
|
||||
int mmio, mmio32, length;
|
||||
int length;
|
||||
unsigned long addr;
|
||||
|
||||
if (!options)
|
||||
return -ENODEV;
|
||||
if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
|
||||
return -EINVAL;
|
||||
|
||||
mmio = !strncmp(options, "mmio,", 5);
|
||||
mmio32 = !strncmp(options, "mmio32,", 7);
|
||||
if (mmio || mmio32) {
|
||||
port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
|
||||
options += mmio ? 5 : 7;
|
||||
addr = simple_strtoul(options, NULL, 0);
|
||||
switch (port->iotype) {
|
||||
case UPIO_MEM32:
|
||||
port->regshift = 2; /* fall-through */
|
||||
case UPIO_MEM:
|
||||
port->mapbase = addr;
|
||||
if (mmio32)
|
||||
port->regshift = 2;
|
||||
} else if (!strncmp(options, "io,", 3)) {
|
||||
port->iotype = UPIO_PORT;
|
||||
options += 3;
|
||||
addr = simple_strtoul(options, NULL, 0);
|
||||
break;
|
||||
case UPIO_PORT:
|
||||
port->iobase = addr;
|
||||
mmio = 0;
|
||||
} else if (!strncmp(options, "0x", 2)) {
|
||||
port->iotype = UPIO_MEM;
|
||||
addr = simple_strtoul(options, NULL, 0);
|
||||
port->mapbase = addr;
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
port->uartclk = BASE_BAUD * 16;
|
||||
|
||||
options = strchr(options, ',');
|
||||
if (options) {
|
||||
options++;
|
||||
device->baud = simple_strtoul(options, NULL, 0);
|
||||
length = min(strcspn(options, " ") + 1,
|
||||
(size_t)(sizeof(device->options)));
|
||||
@ -100,7 +87,7 @@ static int __init parse_options(struct earlycon_device *device,
|
||||
|
||||
if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32)
|
||||
pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
|
||||
mmio32 ? "32" : "",
|
||||
(port->iotype == UPIO_MEM32) ? "32" : "",
|
||||
(unsigned long long)port->mapbase,
|
||||
device->options);
|
||||
else
|
||||
|
@ -1,13 +1,10 @@
|
||||
/*
|
||||
* Driver for Motorola IMX serial ports
|
||||
* Driver for Motorola/Freescale IMX serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Author: Sascha Hauer <sascha@saschahauer.de>
|
||||
* Copyright (C) 2004 Pengutronix
|
||||
*
|
||||
* Copyright (C) 2009 emlix GmbH
|
||||
* Author: Fabian Godehardt (added IrDA support for iMX)
|
||||
* Author: Sascha Hauer <sascha@saschahauer.de>
|
||||
* Copyright (C) 2004 Pengutronix
|
||||
*
|
||||
* 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
|
||||
@ -18,13 +15,6 @@
|
||||
* 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
|
||||
*
|
||||
* [29-Mar-2005] Mike Lee
|
||||
* Added hardware handshake
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
@ -189,7 +179,7 @@
|
||||
|
||||
#define UART_NR 8
|
||||
|
||||
/* i.mx21 type uart runs on all i.mx except i.mx1 */
|
||||
/* i.MX21 type uart runs on all i.mx except i.MX1 and i.MX6q */
|
||||
enum imx_uart_type {
|
||||
IMX1_UART,
|
||||
IMX21_UART,
|
||||
@ -206,10 +196,8 @@ struct imx_port {
|
||||
struct uart_port port;
|
||||
struct timer_list timer;
|
||||
unsigned int old_status;
|
||||
int txirq, rxirq, rtsirq;
|
||||
unsigned int have_rtscts:1;
|
||||
unsigned int dte_mode:1;
|
||||
unsigned int use_irda:1;
|
||||
unsigned int irda_inv_rx:1;
|
||||
unsigned int irda_inv_tx:1;
|
||||
unsigned short trcv_delay; /* transceiver delay */
|
||||
@ -236,12 +224,6 @@ struct imx_port_ucrs {
|
||||
unsigned int ucr3;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IRDA
|
||||
#define USE_IRDA(sport) ((sport)->use_irda)
|
||||
#else
|
||||
#define USE_IRDA(sport) (0)
|
||||
#endif
|
||||
|
||||
static struct imx_uart_data imx_uart_devdata[] = {
|
||||
[IMX1_UART] = {
|
||||
.uts_reg = IMX1_UTS,
|
||||
@ -273,7 +255,7 @@ static struct platform_device_id imx_uart_devtype[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
|
||||
|
||||
static struct of_device_id imx_uart_dt_ids[] = {
|
||||
static const struct of_device_id imx_uart_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
|
||||
{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
|
||||
{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
|
||||
@ -376,48 +358,6 @@ static void imx_stop_tx(struct uart_port *port)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/* half duplex - wait for end of transmission */
|
||||
int n = 256;
|
||||
while ((--n > 0) &&
|
||||
!(readl(sport->port.membase + USR2) & USR2_TXDC)) {
|
||||
udelay(5);
|
||||
barrier();
|
||||
}
|
||||
/*
|
||||
* irda transceiver - wait a bit more to avoid
|
||||
* cutoff, hardware dependent
|
||||
*/
|
||||
udelay(sport->trcv_delay);
|
||||
|
||||
/*
|
||||
* half duplex - reactivate receive mode,
|
||||
* flush receive pipe echo crap
|
||||
*/
|
||||
if (readl(sport->port.membase + USR2) & USR2_TXDC) {
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN);
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp &= ~(UCR4_TCEN);
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
|
||||
while (readl(sport->port.membase + URXD0) &
|
||||
URXD_CHARRDY)
|
||||
barrier();
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_RRDYEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_DREN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are maybe in the SMP context, so if the DMA TX thread is running
|
||||
* on other cpu, we have to wait for it to finish.
|
||||
@ -425,8 +365,23 @@ static void imx_stop_tx(struct uart_port *port)
|
||||
if (sport->dma_is_enabled && sport->dma_is_txing)
|
||||
return;
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
|
||||
temp = readl(port->membase + UCR1);
|
||||
writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);
|
||||
|
||||
/* in rs485 mode disable transmitter if shifter is empty */
|
||||
if (port->rs485.flags & SER_RS485_ENABLED &&
|
||||
readl(port->membase + USR2) & USR2_TXDC) {
|
||||
temp = readl(port->membase + UCR2);
|
||||
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
|
||||
temp &= ~UCR2_CTS;
|
||||
else
|
||||
temp |= UCR2_CTS;
|
||||
writel(temp, port->membase + UCR2);
|
||||
|
||||
temp = readl(port->membase + UCR4);
|
||||
temp &= ~UCR4_TCEN;
|
||||
writel(temp, port->membase + UCR4);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -620,15 +575,18 @@ static void imx_start_tx(struct uart_port *port)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/* half duplex in IrDA mode; have to disable receive mode */
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp &= ~(UCR4_DREN);
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||
/* enable transmitter and shifter empty irq */
|
||||
temp = readl(port->membase + UCR2);
|
||||
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
|
||||
temp &= ~UCR2_CTS;
|
||||
else
|
||||
temp |= UCR2_CTS;
|
||||
writel(temp, port->membase + UCR2);
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~(UCR1_RRDYEN);
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
temp = readl(port->membase + UCR4);
|
||||
temp |= UCR4_TCEN;
|
||||
writel(temp, port->membase + UCR4);
|
||||
}
|
||||
|
||||
if (!sport->dma_is_enabled) {
|
||||
@ -636,16 +594,6 @@ static void imx_start_tx(struct uart_port *port)
|
||||
writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
|
||||
}
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_TRDYEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_TCEN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
}
|
||||
|
||||
if (sport->dma_is_enabled) {
|
||||
if (sport->port.x_char) {
|
||||
/* We have X-char to send, so enable TX IRQ and
|
||||
@ -796,6 +744,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
|
||||
unsigned int sts2;
|
||||
|
||||
sts = readl(sport->port.membase + USR1);
|
||||
sts2 = readl(sport->port.membase + USR2);
|
||||
|
||||
if (sts & USR1_RRDY) {
|
||||
if (sport->dma_is_enabled)
|
||||
@ -804,8 +753,10 @@ static irqreturn_t imx_int(int irq, void *dev_id)
|
||||
imx_rxint(irq, dev_id);
|
||||
}
|
||||
|
||||
if (sts & USR1_TRDY &&
|
||||
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
|
||||
if ((sts & USR1_TRDY &&
|
||||
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
|
||||
(sts2 & USR2_TXDC &&
|
||||
readl(sport->port.membase + UCR4) & UCR4_TCEN))
|
||||
imx_txint(irq, dev_id);
|
||||
|
||||
if (sts & USR1_RTSD)
|
||||
@ -814,11 +765,10 @@ static irqreturn_t imx_int(int irq, void *dev_id)
|
||||
if (sts & USR1_AWAKE)
|
||||
writel(USR1_AWAKE, sport->port.membase + USR1);
|
||||
|
||||
sts2 = readl(sport->port.membase + USR2);
|
||||
if (sts2 & USR2_ORE) {
|
||||
dev_err(sport->port.dev, "Rx FIFO overrun\n");
|
||||
sport->port.icount.overrun++;
|
||||
writel(sts2 | USR2_ORE, sport->port.membase + USR2);
|
||||
writel(USR2_ORE, sport->port.membase + USR2);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -866,11 +816,13 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
temp = readl(sport->port.membase + UCR2) & ~(UCR2_CTS | UCR2_CTSC);
|
||||
if (mctrl & TIOCM_RTS)
|
||||
temp |= UCR2_CTS | UCR2_CTSC;
|
||||
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp &= ~(UCR2_CTS | UCR2_CTSC);
|
||||
if (mctrl & TIOCM_RTS)
|
||||
temp |= UCR2_CTS | UCR2_CTSC;
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
}
|
||||
|
||||
temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
|
||||
if (mctrl & TIOCM_LOOP)
|
||||
@ -1156,9 +1108,6 @@ static int imx_startup(struct uart_port *port)
|
||||
*/
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
|
||||
if (USE_IRDA(sport))
|
||||
temp |= UCR4_IRSC;
|
||||
|
||||
/* set the trigger level for CTS */
|
||||
temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
|
||||
temp |= CTSTL << UCR4_CTSTL_SHF;
|
||||
@ -1181,10 +1130,12 @@ static int imx_startup(struct uart_port *port)
|
||||
imx_uart_dma_init(sport);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
/*
|
||||
* Finally, clear and enable interrupts
|
||||
*/
|
||||
writel(USR1_RTSD, sport->port.membase + USR1);
|
||||
writel(USR2_ORE, sport->port.membase + USR2);
|
||||
|
||||
if (sport->dma_is_inited && !sport->dma_is_enabled)
|
||||
imx_enable_dma(sport);
|
||||
@ -1192,17 +1143,8 @@ static int imx_startup(struct uart_port *port)
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
temp |= UCR1_IREN;
|
||||
temp &= ~(UCR1_RTSDEN);
|
||||
}
|
||||
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
/* Clear any pending ORE flag before enabling interrupt */
|
||||
temp = readl(sport->port.membase + USR2);
|
||||
writel(temp | USR2_ORE, sport->port.membase + USR2);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_OREN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
@ -1219,38 +1161,12 @@ static int imx_startup(struct uart_port *port)
|
||||
writel(temp, sport->port.membase + UCR3);
|
||||
}
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
if (sport->irda_inv_rx)
|
||||
temp |= UCR4_INVR;
|
||||
else
|
||||
temp &= ~(UCR4_INVR);
|
||||
writel(temp | UCR4_DREN, sport->port.membase + UCR4);
|
||||
|
||||
temp = readl(sport->port.membase + UCR3);
|
||||
if (sport->irda_inv_tx)
|
||||
temp |= UCR3_INVT;
|
||||
else
|
||||
temp &= ~(UCR3_INVT);
|
||||
writel(temp, sport->port.membase + UCR3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
imx_enable_ms(&sport->port);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
struct imxuart_platform_data *pdata;
|
||||
pdata = dev_get_platdata(sport->port.dev);
|
||||
sport->irda_inv_rx = pdata->irda_inv_rx;
|
||||
sport->irda_inv_tx = pdata->irda_inv_tx;
|
||||
sport->trcv_delay = pdata->transceiver_delay;
|
||||
if (pdata->irda_enable)
|
||||
pdata->irda_enable(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1286,13 +1202,6 @@ static void imx_shutdown(struct uart_port *port)
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
struct imxuart_platform_data *pdata;
|
||||
pdata = dev_get_platdata(sport->port.dev);
|
||||
if (pdata->irda_enable)
|
||||
pdata->irda_enable(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop our timer.
|
||||
*/
|
||||
@ -1305,8 +1214,6 @@ static void imx_shutdown(struct uart_port *port)
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
|
||||
if (USE_IRDA(sport))
|
||||
temp &= ~(UCR1_IREN);
|
||||
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
@ -1320,7 +1227,7 @@ static void imx_flush_buffer(struct uart_port *port)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
struct scatterlist *sgl = &sport->tx_sgl[0];
|
||||
unsigned long temp;
|
||||
int i = 100, ubir, ubmr, ubrc, uts;
|
||||
int i = 100, ubir, ubmr, uts;
|
||||
|
||||
if (!sport->dma_chan_tx)
|
||||
return;
|
||||
@ -1345,7 +1252,6 @@ static void imx_flush_buffer(struct uart_port *port)
|
||||
*/
|
||||
ubir = readl(sport->port.membase + UBIR);
|
||||
ubmr = readl(sport->port.membase + UBMR);
|
||||
ubrc = readl(sport->port.membase + UBRC);
|
||||
uts = readl(sport->port.membase + IMX21_UTS);
|
||||
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
@ -1358,7 +1264,6 @@ static void imx_flush_buffer(struct uart_port *port)
|
||||
/* Restore the registers */
|
||||
writel(ubir, sport->port.membase + UBIR);
|
||||
writel(ubmr, sport->port.membase + UBMR);
|
||||
writel(ubrc, sport->port.membase + UBRC);
|
||||
writel(uts, sport->port.membase + IMX21_UTS);
|
||||
}
|
||||
|
||||
@ -1374,15 +1279,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
unsigned long num, denom;
|
||||
uint64_t tdiv64;
|
||||
|
||||
/*
|
||||
* If we don't support modem control lines, don't allow
|
||||
* these to be set.
|
||||
*/
|
||||
if (0) {
|
||||
termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
|
||||
termios->c_cflag |= CLOCAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only support CS7 and CS8.
|
||||
*/
|
||||
@ -1401,11 +1297,26 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
if (sport->have_rtscts) {
|
||||
ucr2 &= ~UCR2_IRTS;
|
||||
ucr2 |= UCR2_CTSC;
|
||||
|
||||
if (port->rs485.flags & SER_RS485_ENABLED)
|
||||
/*
|
||||
* RTS is mandatory for rs485 operation, so keep
|
||||
* it under manual control and keep transmitter
|
||||
* disabled.
|
||||
*/
|
||||
if (!(port->rs485.flags &
|
||||
SER_RS485_RTS_AFTER_SEND))
|
||||
ucr2 |= UCR2_CTS;
|
||||
else
|
||||
ucr2 |= UCR2_CTSC;
|
||||
|
||||
} else {
|
||||
termios->c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
}
|
||||
} else if (port->rs485.flags & SER_RS485_ENABLED)
|
||||
/* disable transmitter */
|
||||
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
|
||||
ucr2 |= UCR2_CTS;
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
ucr2 |= UCR2_STPB;
|
||||
@ -1471,24 +1382,16 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
sport->port.membase + UCR2);
|
||||
old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/*
|
||||
* use maximum available submodule frequency to
|
||||
* avoid missing short pulses due to low sampling rate
|
||||
*/
|
||||
div = 1;
|
||||
} else {
|
||||
/* custom-baudrate handling */
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (baud == 38400 && quot != div)
|
||||
baud = sport->port.uartclk / (quot * 16);
|
||||
/* custom-baudrate handling */
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (baud == 38400 && quot != div)
|
||||
baud = sport->port.uartclk / (quot * 16);
|
||||
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (div > 7)
|
||||
div = 7;
|
||||
if (!div)
|
||||
div = 1;
|
||||
}
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (div > 7)
|
||||
div = 7;
|
||||
if (!div)
|
||||
div = 1;
|
||||
|
||||
rational_best_approximation(16 * div * baud, sport->port.uartclk,
|
||||
1 << 16, 1 << 16, &num, &denom);
|
||||
@ -1635,6 +1538,38 @@ static void imx_poll_put_char(struct uart_port *port, unsigned char c)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int imx_rs485_config(struct uart_port *port,
|
||||
struct serial_rs485 *rs485conf)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
/* unimplemented */
|
||||
rs485conf->delay_rts_before_send = 0;
|
||||
rs485conf->delay_rts_after_send = 0;
|
||||
rs485conf->flags |= SER_RS485_RX_DURING_TX;
|
||||
|
||||
/* RTS is required to control the transmitter */
|
||||
if (!sport->have_rtscts)
|
||||
rs485conf->flags &= ~SER_RS485_ENABLED;
|
||||
|
||||
if (rs485conf->flags & SER_RS485_ENABLED) {
|
||||
unsigned long temp;
|
||||
|
||||
/* disable transmitter */
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp &= ~UCR2_CTSC;
|
||||
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
|
||||
temp &= ~UCR2_CTS;
|
||||
else
|
||||
temp |= UCR2_CTS;
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
}
|
||||
|
||||
port->rs485 = *rs485conf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct uart_ops imx_pops = {
|
||||
.tx_empty = imx_tx_empty,
|
||||
.set_mctrl = imx_set_mctrl,
|
||||
@ -1927,9 +1862,6 @@ static int serial_imx_probe_dt(struct imx_port *sport,
|
||||
if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
|
||||
sport->have_rtscts = 1;
|
||||
|
||||
if (of_get_property(np, "fsl,irda-mode", NULL))
|
||||
sport->use_irda = 1;
|
||||
|
||||
if (of_get_property(np, "fsl,dte-mode", NULL))
|
||||
sport->dte_mode = 1;
|
||||
|
||||
@ -1958,9 +1890,6 @@ static void serial_imx_probe_pdata(struct imx_port *sport,
|
||||
|
||||
if (pdata->flags & IMXUART_HAVE_RTSCTS)
|
||||
sport->have_rtscts = 1;
|
||||
|
||||
if (pdata->flags & IMXUART_IRDA)
|
||||
sport->use_irda = 1;
|
||||
}
|
||||
|
||||
static int serial_imx_probe(struct platform_device *pdev)
|
||||
@ -1969,6 +1898,7 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||
void __iomem *base;
|
||||
int ret = 0;
|
||||
struct resource *res;
|
||||
int txirq, rxirq, rtsirq;
|
||||
|
||||
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
|
||||
if (!sport)
|
||||
@ -1985,17 +1915,21 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
rxirq = platform_get_irq(pdev, 0);
|
||||
txirq = platform_get_irq(pdev, 1);
|
||||
rtsirq = platform_get_irq(pdev, 2);
|
||||
|
||||
sport->port.dev = &pdev->dev;
|
||||
sport->port.mapbase = res->start;
|
||||
sport->port.membase = base;
|
||||
sport->port.type = PORT_IMX,
|
||||
sport->port.iotype = UPIO_MEM;
|
||||
sport->port.irq = platform_get_irq(pdev, 0);
|
||||
sport->rxirq = platform_get_irq(pdev, 0);
|
||||
sport->txirq = platform_get_irq(pdev, 1);
|
||||
sport->rtsirq = platform_get_irq(pdev, 2);
|
||||
sport->port.irq = rxirq;
|
||||
sport->port.fifosize = 32;
|
||||
sport->port.ops = &imx_pops;
|
||||
sport->port.rs485_config = imx_rs485_config;
|
||||
sport->port.rs485.flags =
|
||||
SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
|
||||
sport->port.flags = UPF_BOOT_AUTOCONF;
|
||||
init_timer(&sport->timer);
|
||||
sport->timer.function = imx_timeout;
|
||||
@ -2021,27 +1955,18 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
|
||||
* chips only have one interrupt.
|
||||
*/
|
||||
if (sport->txirq > 0) {
|
||||
ret = devm_request_irq(&pdev->dev, sport->rxirq, imx_rxint, 0,
|
||||
if (txirq > 0) {
|
||||
ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
|
||||
dev_name(&pdev->dev), sport);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, sport->txirq, imx_txint, 0,
|
||||
ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
|
||||
dev_name(&pdev->dev), sport);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* do not use RTS IRQ on IrDA */
|
||||
if (!USE_IRDA(sport)) {
|
||||
ret = devm_request_irq(&pdev->dev, sport->rtsirq,
|
||||
imx_rtsint, 0,
|
||||
dev_name(&pdev->dev), sport);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = devm_request_irq(&pdev->dev, sport->port.irq, imx_int, 0,
|
||||
ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
|
||||
dev_name(&pdev->dev), sport);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -782,7 +782,7 @@ static int max3100_probe(struct spi_device *spi)
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
max3100s[i]->crystal = pdata->crystal;
|
||||
max3100s[i]->loopback = pdata->loopback;
|
||||
max3100s[i]->poll_time = pdata->poll_time * HZ / 1000;
|
||||
max3100s[i]->poll_time = msecs_to_jiffies(pdata->poll_time);
|
||||
if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0)
|
||||
max3100s[i]->poll_time = 1;
|
||||
max3100s[i]->max3100_hw_suspend = pdata->max3100_hw_suspend;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1155,14 +1155,14 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
|
||||
static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
|
||||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
s->gpios = mctrl_gpio_init(dev, 0);
|
||||
if (IS_ERR_OR_NULL(s->gpios))
|
||||
return false;
|
||||
if (IS_ERR(s->gpios))
|
||||
return PTR_ERR(s->gpios);
|
||||
|
||||
/* Block (enabled before) DMA option if RTS or CTS is GPIO line */
|
||||
if (!RTS_AT_AUART() || !CTS_AT_AUART()) {
|
||||
@ -1180,7 +1180,7 @@ static bool mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
|
||||
s->gpio_irq[i] = -EINVAL;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_auart_free_gpio_irq(struct mxs_auart_port *s)
|
||||
@ -1276,9 +1276,11 @@ static int mxs_auart_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, s);
|
||||
|
||||
if (!mxs_auart_init_gpios(s, &pdev->dev))
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to initialize GPIOs. The serial port may not work as expected\n");
|
||||
ret = mxs_auart_init_gpios(s, &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to initialize GPIOs.\n");
|
||||
got out_free_irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the GPIO lines IRQ
|
||||
|
@ -1118,8 +1118,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg)
|
||||
|
||||
cprev = cnow;
|
||||
}
|
||||
|
||||
current->state = TASK_RUNNING;
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&port->delta_msr_wait, &wait);
|
||||
|
||||
return ret;
|
||||
@ -1809,6 +1808,52 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
|
||||
return ports + idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* uart_parse_earlycon - Parse earlycon options
|
||||
* @p: ptr to 2nd field (ie., just beyond '<name>,')
|
||||
* @iotype: ptr for decoded iotype (out)
|
||||
* @addr: ptr for decoded mapbase/iobase (out)
|
||||
* @options: ptr for <options> field; NULL if not present (out)
|
||||
*
|
||||
* Decodes earlycon kernel command line parameters of the form
|
||||
* earlycon=<name>,io|mmio|mmio32,<addr>,<options>
|
||||
* console=<name>,io|mmio|mmio32,<addr>,<options>
|
||||
*
|
||||
* The optional form
|
||||
* earlycon=<name>,0x<addr>,<options>
|
||||
* console=<name>,0x<addr>,<options>
|
||||
* is also accepted; the returned @iotype will be UPIO_MEM.
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on failure
|
||||
*/
|
||||
int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
|
||||
char **options)
|
||||
{
|
||||
if (strncmp(p, "mmio,", 5) == 0) {
|
||||
*iotype = UPIO_MEM;
|
||||
p += 5;
|
||||
} else if (strncmp(p, "mmio32,", 7) == 0) {
|
||||
*iotype = UPIO_MEM32;
|
||||
p += 7;
|
||||
} else if (strncmp(p, "io,", 3) == 0) {
|
||||
*iotype = UPIO_PORT;
|
||||
p += 3;
|
||||
} else if (strncmp(p, "0x", 2) == 0) {
|
||||
*iotype = UPIO_MEM;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*addr = simple_strtoul(p, NULL, 0);
|
||||
p = strchr(p, ',');
|
||||
if (p)
|
||||
p++;
|
||||
|
||||
*options = p;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uart_parse_earlycon);
|
||||
|
||||
/**
|
||||
* uart_parse_options - Parse serial port baud/parity/bits/flow control.
|
||||
* @options: pointer to option string
|
||||
|
@ -48,9 +48,6 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
|
||||
int value_array[UART_GPIO_MAX];
|
||||
unsigned int count = 0;
|
||||
|
||||
if (IS_ERR_OR_NULL(gpios))
|
||||
return;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++)
|
||||
if (!IS_ERR_OR_NULL(gpios->gpio[i]) &&
|
||||
mctrl_gpios_desc[i].dir_out) {
|
||||
@ -65,10 +62,7 @@ EXPORT_SYMBOL_GPL(mctrl_gpio_set);
|
||||
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
|
||||
enum mctrl_gpio_idx gidx)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(gpios) && !IS_ERR_OR_NULL(gpios->gpio[gidx]))
|
||||
return gpios->gpio[gidx];
|
||||
else
|
||||
return NULL;
|
||||
return gpios->gpio[gidx];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
|
||||
|
||||
@ -76,15 +70,8 @@ unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
|
||||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
|
||||
/*
|
||||
* return it unchanged if the structure is not allocated
|
||||
*/
|
||||
if (IS_ERR_OR_NULL(gpios))
|
||||
return *mctrl;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
if (!IS_ERR_OR_NULL(gpios->gpio[i]) &&
|
||||
!mctrl_gpios_desc[i].dir_out) {
|
||||
if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
|
||||
if (gpiod_get_value(gpios->gpio[i]))
|
||||
*mctrl |= mctrl_gpios_desc[i].mctrl;
|
||||
else
|
||||
@ -107,27 +94,20 @@ struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
gpios->gpio[i] = devm_gpiod_get_index(dev,
|
||||
mctrl_gpios_desc[i].name,
|
||||
idx);
|
||||
|
||||
/*
|
||||
* The GPIOs are maybe not all filled,
|
||||
* this is not an error.
|
||||
*/
|
||||
if (IS_ERR_OR_NULL(gpios->gpio[i]))
|
||||
continue;
|
||||
enum gpiod_flags flags;
|
||||
|
||||
if (mctrl_gpios_desc[i].dir_out)
|
||||
err = gpiod_direction_output(gpios->gpio[i], 0);
|
||||
flags = GPIOD_OUT_LOW;
|
||||
else
|
||||
err = gpiod_direction_input(gpios->gpio[i]);
|
||||
if (err) {
|
||||
dev_dbg(dev, "Unable to set direction for %s GPIO",
|
||||
mctrl_gpios_desc[i].name);
|
||||
devm_gpiod_put(dev, gpios->gpio[i]);
|
||||
gpios->gpio[i] = NULL;
|
||||
}
|
||||
flags = GPIOD_IN;
|
||||
|
||||
gpios->gpio[i] =
|
||||
devm_gpiod_get_index_optional(dev,
|
||||
mctrl_gpios_desc[i].name,
|
||||
idx, flags);
|
||||
|
||||
if (IS_ERR(gpios->gpio[i]))
|
||||
return PTR_ERR(gpios->gpio[i]);
|
||||
}
|
||||
|
||||
return gpios;
|
||||
@ -138,9 +118,6 @@ void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
|
||||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
|
||||
if (IS_ERR_OR_NULL(gpios))
|
||||
return;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++)
|
||||
if (!IS_ERR_OR_NULL(gpios->gpio[i]))
|
||||
devm_gpiod_put(dev, gpios->gpio[i]);
|
||||
|
@ -493,6 +493,8 @@ static int sprd_verify_port(struct uart_port *port,
|
||||
return -EINVAL;
|
||||
if (port->irq != ser->irq)
|
||||
return -EINVAL;
|
||||
if (port->iotype != ser->io_type)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -707,7 +709,7 @@ static int sprd_probe(struct platform_device *pdev)
|
||||
up->dev = &pdev->dev;
|
||||
up->line = index;
|
||||
up->type = PORT_SPRD;
|
||||
up->iotype = SERIAL_IO_PORT;
|
||||
up->iotype = UPIO_MEM;
|
||||
up->uartclk = SPRD_DEF_RATE;
|
||||
up->fifosize = SPRD_FIFO_SIZE;
|
||||
up->ops = &serial_sprd_ops;
|
||||
@ -754,6 +756,7 @@ static int sprd_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sprd_suspend(struct device *dev)
|
||||
{
|
||||
struct sprd_uart_port *sup = dev_get_drvdata(dev);
|
||||
@ -771,6 +774,7 @@ static int sprd_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume);
|
||||
|
||||
|
@ -1154,7 +1154,8 @@ static int __init cdns_uart_console_setup(struct console *co, char *options)
|
||||
return -EINVAL;
|
||||
|
||||
if (!port->mapbase) {
|
||||
pr_debug("console on ttyPS%i not present\n", co->index);
|
||||
pr_debug("console on " CDNS_UART_TTY_NAME "%i not present\n",
|
||||
co->index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -1237,7 +1237,7 @@ static void default_attr(struct vc_data *vc)
|
||||
|
||||
struct rgb { u8 r; u8 g; u8 b; };
|
||||
|
||||
struct rgb rgb_from_256(int i)
|
||||
static struct rgb rgb_from_256(int i)
|
||||
{
|
||||
struct rgb c;
|
||||
if (i < 8) { /* Standard colours. */
|
||||
@ -1573,7 +1573,7 @@ static void setterm_command(struct vc_data *vc)
|
||||
case 11: /* set bell duration in msec */
|
||||
if (vc->vc_npar >= 1)
|
||||
vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
|
||||
vc->vc_par[1] * HZ / 1000 : 0;
|
||||
msecs_to_jiffies(vc->vc_par[1]) : 0;
|
||||
else
|
||||
vc->vc_bell_duration = DEFAULT_BELL_DURATION;
|
||||
break;
|
||||
|
@ -388,7 +388,7 @@ int vt_ioctl(struct tty_struct *tty,
|
||||
* Generate the tone for the appropriate number of ticks.
|
||||
* If the time is zero, turn off sound ourselves.
|
||||
*/
|
||||
ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
|
||||
ticks = msecs_to_jiffies((arg >> 16) & 0xffff);
|
||||
count = ticks ? (arg & 0xffff) : 0;
|
||||
if (count)
|
||||
count = PIT_TICK_RATE / count;
|
||||
|
48
include/linux/dma/hsu.h
Normal file
48
include/linux/dma/hsu.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Driver for the High Speed UART DMA
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _DMA_HSU_H
|
||||
#define _DMA_HSU_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/platform_data/dma-hsu.h>
|
||||
|
||||
struct hsu_dma;
|
||||
|
||||
/**
|
||||
* struct hsu_dma_chip - representation of HSU DMA hardware
|
||||
* @dev: struct device of the DMA controller
|
||||
* @irq: irq line
|
||||
* @regs: memory mapped I/O space
|
||||
* @length: I/O space length
|
||||
* @offset: offset of the I/O space where registers are located
|
||||
* @hsu: struct hsu_dma that is filed by ->probe()
|
||||
* @pdata: platform data for the DMA controller if provided
|
||||
*/
|
||||
struct hsu_dma_chip {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
void __iomem *regs;
|
||||
unsigned int length;
|
||||
unsigned int offset;
|
||||
struct hsu_dma *hsu;
|
||||
struct hsu_dma_platform_data *pdata;
|
||||
};
|
||||
|
||||
/* Export to the internal users */
|
||||
irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr);
|
||||
|
||||
/* Export to the platform drivers */
|
||||
int hsu_dma_probe(struct hsu_dma_chip *chip);
|
||||
int hsu_dma_remove(struct hsu_dma_chip *chip);
|
||||
|
||||
#endif /* _DMA_HSU_H */
|
25
include/linux/platform_data/dma-hsu.h
Normal file
25
include/linux/platform_data/dma-hsu.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Driver for the High Speed UART DMA
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _PLATFORM_DATA_DMA_HSU_H
|
||||
#define _PLATFORM_DATA_DMA_HSU_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
struct hsu_dma_slave {
|
||||
struct device *dma_dev;
|
||||
int chan_id;
|
||||
};
|
||||
|
||||
struct hsu_dma_platform_data {
|
||||
unsigned short nr_channels;
|
||||
};
|
||||
|
||||
#endif /* _PLATFORM_DATA_DMA_HSU_H */
|
@ -20,14 +20,9 @@
|
||||
#define ASMARM_ARCH_UART_H
|
||||
|
||||
#define IMXUART_HAVE_RTSCTS (1<<0)
|
||||
#define IMXUART_IRDA (1<<1)
|
||||
|
||||
struct imxuart_platform_data {
|
||||
unsigned int flags;
|
||||
void (*irda_enable)(int enable);
|
||||
unsigned int irda_inv_rx:1;
|
||||
unsigned int irda_inv_tx:1;
|
||||
unsigned short transceiver_delay;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -354,6 +354,8 @@ early_param("earlycon", name ## _setup_earlycon);
|
||||
|
||||
struct uart_port *uart_get_console(struct uart_port *ports, int nr,
|
||||
struct console *c);
|
||||
int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
|
||||
char **options);
|
||||
void uart_parse_options(char *options, int *baud, int *parity, int *bits,
|
||||
int *flow);
|
||||
int uart_set_options(struct uart_port *port, struct console *co, int baud,
|
||||
|
@ -1,47 +0,0 @@
|
||||
#ifndef _SERIAL_MFD_H_
|
||||
#define _SERIAL_MFD_H_
|
||||
|
||||
/* HW register offset definition */
|
||||
#define UART_FOR 0x08
|
||||
#define UART_PS 0x0C
|
||||
#define UART_MUL 0x0D
|
||||
#define UART_DIV 0x0E
|
||||
|
||||
#define HSU_GBL_IEN 0x0
|
||||
#define HSU_GBL_IST 0x4
|
||||
|
||||
#define HSU_GBL_INT_BIT_PORT0 0x0
|
||||
#define HSU_GBL_INT_BIT_PORT1 0x1
|
||||
#define HSU_GBL_INT_BIT_PORT2 0x2
|
||||
#define HSU_GBL_INT_BIT_IRI 0x3
|
||||
#define HSU_GBL_INT_BIT_HDLC 0x4
|
||||
#define HSU_GBL_INT_BIT_DMA 0x5
|
||||
|
||||
#define HSU_GBL_ISR 0x8
|
||||
#define HSU_GBL_DMASR 0x400
|
||||
#define HSU_GBL_DMAISR 0x404
|
||||
|
||||
#define HSU_PORT_REG_OFFSET 0x80
|
||||
#define HSU_PORT0_REG_OFFSET 0x80
|
||||
#define HSU_PORT1_REG_OFFSET 0x100
|
||||
#define HSU_PORT2_REG_OFFSET 0x180
|
||||
#define HSU_PORT_REG_LENGTH 0x80
|
||||
|
||||
#define HSU_DMA_CHANS_REG_OFFSET 0x500
|
||||
#define HSU_DMA_CHANS_REG_LENGTH 0x40
|
||||
|
||||
#define HSU_CH_SR 0x0 /* channel status reg */
|
||||
#define HSU_CH_CR 0x4 /* control reg */
|
||||
#define HSU_CH_DCR 0x8 /* descriptor control reg */
|
||||
#define HSU_CH_BSR 0x10 /* max fifo buffer size reg */
|
||||
#define HSU_CH_MOTSR 0x14 /* minimum ocp transfer size */
|
||||
#define HSU_CH_D0SAR 0x20 /* desc 0 start addr */
|
||||
#define HSU_CH_D0TSR 0x24 /* desc 0 transfer size */
|
||||
#define HSU_CH_D1SAR 0x28
|
||||
#define HSU_CH_D1TSR 0x2C
|
||||
#define HSU_CH_D2SAR 0x30
|
||||
#define HSU_CH_D2TSR 0x34
|
||||
#define HSU_CH_D3SAR 0x38
|
||||
#define HSU_CH_D3TSR 0x3C
|
||||
|
||||
#endif
|
@ -241,25 +241,6 @@
|
||||
#define UART_FCR_PXAR16 0x80 /* receive FIFO threshold = 16 */
|
||||
#define UART_FCR_PXAR32 0xc0 /* receive FIFO threshold = 32 */
|
||||
|
||||
/*
|
||||
* Intel MID on-chip HSU (High Speed UART) defined bits
|
||||
*/
|
||||
#define UART_FCR_HSU_64_1B 0x00 /* receive FIFO treshold = 1 */
|
||||
#define UART_FCR_HSU_64_16B 0x40 /* receive FIFO treshold = 16 */
|
||||
#define UART_FCR_HSU_64_32B 0x80 /* receive FIFO treshold = 32 */
|
||||
#define UART_FCR_HSU_64_56B 0xc0 /* receive FIFO treshold = 56 */
|
||||
|
||||
#define UART_FCR_HSU_16_1B 0x00 /* receive FIFO treshold = 1 */
|
||||
#define UART_FCR_HSU_16_4B 0x40 /* receive FIFO treshold = 4 */
|
||||
#define UART_FCR_HSU_16_8B 0x80 /* receive FIFO treshold = 8 */
|
||||
#define UART_FCR_HSU_16_14B 0xc0 /* receive FIFO treshold = 14 */
|
||||
|
||||
#define UART_FCR_HSU_64B_FIFO 0x20 /* chose 64 bytes FIFO */
|
||||
#define UART_FCR_HSU_16B_FIFO 0x00 /* chose 16 bytes FIFO */
|
||||
|
||||
#define UART_FCR_HALF_EMPT_TXI 0x00 /* trigger TX_EMPT IRQ for half empty */
|
||||
#define UART_FCR_FULL_EMPT_TXI 0x08 /* trigger TX_EMPT IRQ for full empty */
|
||||
|
||||
/*
|
||||
* These register definitions are for the 16C950
|
||||
*/
|
||||
|
@ -2480,7 +2480,6 @@ void register_console(struct console *newcon)
|
||||
newcon->setup(newcon, console_cmdline[i].options) != 0)
|
||||
break;
|
||||
newcon->flags |= CON_ENABLED;
|
||||
newcon->index = c->index;
|
||||
if (i == selected_console) {
|
||||
newcon->flags |= CON_CONSDEV;
|
||||
preferred_console = selected_console;
|
||||
|
Loading…
Reference in New Issue
Block a user