Hello Kathy Chang, I will have a look at your changes. Please send your mails also to spi-devel-general@xxxxxxxxxxxxxxxxxxxxx and spi-devel-general@xxxxxxxxxxxxxxxxxxxxx (simply press reply-to-all). Also remember that you should not simply remove existing copyright notices when you modify driver! Regards, Manuel Stahl
hello ,Manuel Stahl: I deleted the part of the IO driver.And I compile the driver from the kernel independent. Please refer to the attached source code. kathy chang 2010.12.20 ======= 2010-12-20 10:09:00 æåæääåéï=======>Hello Kathy Chang, > >thank you for your effort. Can you provide a patch for the sc16is7x2.c file? > >Regards, >Manuel Stahl > >On 20.12.2010 10:01, changgx wrote:>> hello,Manuel Stahl:>> >> Sc16is7x2 driver has been successfully debugged. >> >> In sc16is7x2.cïthe control to the var .cs_change does not match with at91sam9260 driver. >> >> 1ã>> In function sc16is7x2_startup(),the spi chip select maintains low level after the setence cmds = sc16is7x2_alloc_spi_cmds(8). >> It caused a error about next SPI operation.>> >> Improved method is to use 8 sc16is7x2_write_async() function to set sc16is7x2 reg. >> >> 2ã >> >> In sc16is7x2_msg_add_fifo_tx() functionï you need to take anti-cs_change variables. >> >> >> good luck!!! >> >> >> ======= 2010-12-14 11:52:00 æåæääåéï======= >>>> >> = = = = = = = = = = = = = = = = = = = =>>> Hello again, >>> >>> The commands in sc16is7x2_spi_async are sent async (as the name says), >>> so the first time you execute sc16is7x2_read(ts->spi, UART_IER,ch) the >>> async part was not yet completed, the second time it was completed and >>> has set UART_IER to 0. >>> >>> Regards, >>> Manuel >>> >>> On 13.12.2010 06:43, changgx wrote:>>>> helloïmanuel.stahlï >>>> >>>> excuse me!! >>>> >>>> I found the sc16is7x2.c driver provided by you on the internet the >>>> other day. And I explante it to my board. >>>> >>>> My develop environment is about ïCPU for AT91SAM9260 and kennel for >>>> linux-2.6.27. >>>> >>>> But I found some questions in debugging. >>>> >>>> In the function of sc16is7x2_startup( ), i add the sentence to read >>>> register value after sc16is7x2_spi_async(ts->spi, cmds, 8) sentence >>>> and i find that the return value is always 0xff. >>>> >>>> for example: >>>> >>>> int uier1,uier2; >>>> >>>> uier1 = sc16is7x2_read(ts->spi, UART_IER,ch); >>>> SC16_PRINTK("+++++++++++++++++++++> s ïd UART_IER = x >>>> \n",__func__,__LINE__,uier1); >>>> uier2 = sc16is7x2_read(ts->spi, UART_IER,ch); >>>> SC16_PRINTK("+++++++++++++++++++++> s ïd UART_IER = x >>>> \n",__func__,__LINE__,uier2); >>>> >>>> the print result: >>>> >>>> +++++++++++++++++++++> sc16is7x2_startup : 512 UART_IER = ff >>>> +++++++++++++++++++++> sc16is7x2_startup : 515 UART_IER = 0 >>>> >>>> The results is that the first time is 0xff, the secend is 0. >>>> >>>> And it is same to read other register. >>>> >>>> please give me some advice for this question. >>>> >>>> ps: thank you very much for your open souce and look forward to your >>>> email. >>>> >>>> thanksï >>>> >>>> kathy chang >>>> >>>> 2010-12-13 >>>> >>>> ------------------------------------------------ >>>> >>>> Emailïchanggx@xxxxxxx<mailto:changgx@xxxxxxx> >>>> >>>> ------------------------------------------------ >>>> >>>> >>>> >>>> = = = = = = = = = = = = = = = = = = = = >>>> ããããããããè >>>> çï >>>> changgx >>>> changgx@xxxxxxx<mailto:changgx@xxxxxxx> >>>> 2010-12-13 >>>> ãããããããããããããã>>> >>>>>> -- >>> Manuel Stahl>>> Fraunhofer-Institut IIS >>> Leistungsoptimierte Systeme >>> >>> Nordostpark 93 >>> D90411 NÃrnberg >>> Telefon +49 (0)911/58061-6419 >>> Fax +49 (0)911/58061-6398 >>> E-Mail manuel.stahl@xxxxxxxxxxxxxxxxx >>> >>> http://www.iis.fraunhofer.de >>> http://www.smart-power.fraunhofer.de >>> >>> >>> >>> __________ Information from ESET Smart Security, version of virus signature database 5563 (20101026) __________ >>> >>> The message was checked by ESET Smart Security. >>> >>> http://www.eset.com>>>> >> ããããããããè>> çï>> >>>> ããããããããchanggx >> ããããããããchanggx@xxxxxxx >> ãããããããããã2010-12-20>> >> >> >>> >>-- >Manuel Stahl>Fraunhofer-Institut IIS >Leistungsoptimierte Systeme > >Nordostpark 93 >D90411 NÃrnberg >Telefon +49 (0)911/58061-6419 >Fax +49 (0)911/58061-6398 >E-Mail manuel.stahl@xxxxxxxxxxxxxxxxx > >http://www.iis.fraunhofer.de >http://www.smart-power.fraunhofer.de > > > >__________ Information from ESET Smart Security, version of virus signature database 5563 (20101026) __________ > >The message was checked by ESET Smart Security. > >http://www.eset.com= = = = = = = = = = = = = = = = = = = = ããããããããè çïããããããããchanggxããããããããchanggx@xxxxxxx ãããããããããã2010-12-20 sa16is7x2.h NÂnârÂÂÃÃ)emÃhÃyhiÃÂÂw^âÂà sc16is7x2.c NÂnârÂÂÃÃ)emÃhÃyhiÃÂÂw^âÂÃ
Attachment:
Makefile
Description: Binary data
#ifndef LINUX_SPI_SC16IS752_H #define LINUX_SPI_SC16IS752_H struct sc16is7x2_platform_data { unsigned int uartclk; /* uart line number of the first channel */ unsigned uart_base; }; #endif
/** * drivers/serial/sc16is7x2.c * * Copyright (C) 2010 kathy chang changgx@xxxxxxx * * 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. * * The SC16IS7x2 device is a SPI driven dual UART with GPIOs. * * The driver exports two uarts . */ #define DEBUG #include <linux/irq.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/spi/spi.h> #include <linux/freezer.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/gpio.h> #include <linux/serial.h> #include "sc16is7x2.h" #define H_SPI_SPEED (2*1000*1000) #define SC16IS7X2_MAJOR 204 #define SC16IS7X2_MINOR 209 #define MAX_SC16IS7X2 2 #define FIFO_SIZE 64 #define PMC_PCER 0x10 #define DRIVER_NAME "sc16is7x2" #define TYPE_NAME "SC16IS7x2" #define REG_READ 0x80 #define REG_WRITE 0x00 /* Special registers */ #define REG_TXLVL 0x08 /* Transmitter FIFO Level register */ #define REG_RXLVL 0x09 /* Receiver FIFO Level register */ #define REG_IOD 0x0A /* IO Direction register */ #define REG_IOS 0x0B /* IO State register */ #define REG_IOI 0x0C /* IO Interrupt Enable register */ #define REG_IOC 0x0E /* IO Control register */ #define IOC_SRESET 0x08 /* Software reset */ #define IOC_GPIO30 0x04 /* GPIO 3:0 unset: as IO, set: as modem pins */ #define IOC_GPIO74 0x02 /* GPIO 7:4 unset: as IO, set: as modem pins */ #define IOC_IOLATCH 0x01 /* Unset: input unlatched, set: input latched */ /* Redefine some MCR bits */ #ifdef UART_MCR_TCRTLR #undef UART_MCR_TCRTLR #endif #define UART_MCR_TCRTLR 0x04 #define UART_MCR_IRDA 0x40 #define WRITE_CMD(reg, ch) (REG_WRITE | (reg & 0xf) << 3 | (ch & 0x1) << 1) #define READ_CMD(reg, ch) (REG_READ | (reg & 0xf) << 3 | (ch & 0x1) << 1) static struct sc16is7x2_platform_data sc16is7x2_spi_data = { .uartclk = 18432000, .uart_base = 0, }; /* 16bit SPI command to read or write a register */ struct sc16is7x2_spi_reg { u8 cmd; u8 value; } __attribute__ ((packed)); struct sc16is7x2_chip; /* * Some registers must be read back to modify. * To save time we cache them here in memory */ struct sc16is7x2_channel { struct sc16is7x2_chip *chip; /* back link */ struct mutex lock; struct uart_port uart; struct spi_transfer fifo_rx; struct spi_transfer fifo_tx[3]; u8 iir; u8 lsr; u8 msr; u8 ier; /* cache for IER register */ u8 fcr; /* cache for FCR register */ u8 lcr; /* cache for LCR register */ u8 mcr; /* cache for MCR register */ u8 rxbuf[FIFO_SIZE+1]; u8 write_fifo_cmd; u8 read_fifo_cmd; bool active; }; struct sc16is7x2_chip { struct spi_device *spi; struct mutex lock; struct sc16is7x2_channel channel[2]; /* for handling irqs: need workqueue since we do spi_sync */ struct workqueue_struct *workqueue; struct work_struct work; /* set to 1 to make the workhandler exit as soon as possible */ int force_end_work; /* need to know we are suspending to avoid deadlock on workqueue */ int suspending; struct spi_message fifo_message; #define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */ #define UART_BUG_NOMSR BIT(2) /* UART has buggy MSR status bits (Au1x00) */ #define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */ u16 bugs; /* port bugs */ #define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS u8 lsr_saved_flags; #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA u8 msr_saved_flags; }; /* ******************************** SPI ********************************* */ /* * Reserve memory for command sequence * @param cnt number of commands */ static inline struct sc16is7x2_spi_reg * sc16is7x2_alloc_spi_cmds(unsigned cnt) { return kzalloc(sizeof(struct sc16is7x2_spi_reg)*cnt, GFP_KERNEL); } /* * sc16is7x2_add_write_cmd - Add write command to sequence */ static inline void sc16is7x2_add_write_cmd(struct sc16is7x2_spi_reg *cmd, u8 reg, u8 ch, u8 value) { cmd->cmd = WRITE_CMD(reg, ch); cmd->value = value; } /* * sc16is7x2_add_read_cmd - Add read command to sequence */ static inline void sc16is7x2_add_read_cmd(struct sc16is7x2_spi_reg *cmd, u8 reg, u8 ch) { cmd->cmd = READ_CMD(reg, ch); cmd->value = 0; } /* * sc16is7x2_complete - Completion handler for async SPI transfers */ static void sc16is7x2_complete(void *context) { struct spi_message *m = context; u8 *tx_chain = m->state; kfree(tx_chain); kfree(m); } /* * sc16is7x2_spi_async - Send command sequence */ static int sc16is7x2_spi_async(struct spi_device *spi, struct sc16is7x2_spi_reg *cmds, unsigned len) { struct spi_transfer *t; struct spi_message *m; m = spi_message_alloc(len, GFP_KERNEL); if (!m) return -ENOMEM; m->complete = sc16is7x2_complete; m->context = m; m->state = cmds; list_for_each_entry(t, &m->transfers, transfer_list) { t->tx_buf = (u8 *)cmds; t->len = 2; t->cs_change = false; //t->cs_change = true; cmds++; } return spi_async(spi, m); } /* * sc16is7x2_write_async - Write a new register content (async) */ static inline int sc16is7x2_write_async(struct spi_device *spi, u8 reg, u8 ch, u8 value) { struct sc16is7x2_spi_reg *cmd = sc16is7x2_alloc_spi_cmds(1); if (!cmd) return -ENOMEM; sc16is7x2_add_write_cmd(cmd, reg, ch, value); return sc16is7x2_spi_async(spi, cmd, 1); } /* * sc16is7x2_write - Write a new register content (sync) */ static int sc16is7x2_write(struct spi_device *spi, u8 reg, u8 ch, u8 val) { u16 word = REG_WRITE | (reg & 0xf) << 3 | (ch & 0x3) << 1 | val << 8; return spi_write(spi, (const u8 *)&word, sizeof(word)); } /** * sc16is7x2_read - Read back register content * @spi: The SPI device * @reg: Register offset * * Returns positive 8 bit value from device if successful or a * negative value on error */ static int sc16is7x2_read(struct spi_device *spi, unsigned reg, unsigned ch) { u8 cmd = REG_READ | (reg & 0xf) << 3 | (ch & 0x3) << 1; return spi_w8r8(spi, cmd); } /* ******************************** UART ********************************* */ /* Uart divisor latch write */ static inline void sc16is7x2_add_dl_write_cmd(struct sc16is7x2_spi_reg *cmd, u8 ch, int value) { sc16is7x2_add_write_cmd(&cmd[0], UART_DLL, ch, value & 0xff); sc16is7x2_add_write_cmd(&cmd[1], UART_DLM, ch, (value >> 8) & 0xff); } static unsigned int sc16is7x2_tx_empty(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned lsr; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); mutex_lock(&chan->lock); lsr = chan->lsr; mutex_unlock(&chan->lock); return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0; } static unsigned int sc16is7x2_get_mctrl(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned int status; unsigned int ret; dev_dbg(&ts->spi->dev, "%s\n", __func__); printk(KERN_DEBUG "---------> function call %s\n", __func__); status = chan->msr; ret = 0; if (status & UART_MSR_DCD) ret |= TIOCM_CAR; if (status & UART_MSR_RI) ret |= TIOCM_RNG; if (status & UART_MSR_DSR) ret |= TIOCM_DSR; if (status & UART_MSR_CTS) ret |= TIOCM_CTS; return ret; } static unsigned int __set_mctrl(unsigned int mctrl) { unsigned char mcr = 0; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; if (mctrl & TIOCM_DTR) mcr |= UART_MCR_DTR; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; return mcr; } static void sc16is7x2_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); sc16is7x2_write_async(ts->spi, UART_MCR, ch, __set_mctrl(mctrl)); } static inline void __stop_tx(struct sc16is7x2_channel *chan) { struct sc16is7x2_chip *ts = chan->chip; unsigned ch = chan->uart.line & 0x01; if (chan->ier & UART_IER_THRI) { chan->ier &= ~UART_IER_THRI; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } } static void sc16is7x2_stop_tx(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); __stop_tx(chan); } /* open */ static void sc16is7x2_start_tx(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); if (!(chan->ier & UART_IER_THRI)) { chan->ier |= UART_IER_THRI; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } } static void sc16is7x2_stop_rx(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); chan->ier &= ~UART_IER_RLSI; chan->uart.read_status_mask &= ~UART_LSR_DR; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } static void sc16is7x2_enable_ms(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; dev_dbg(&ts->spi->dev, "%s\n", __func__); printk(KERN_DEBUG "---------> function call %s\n", __func__); chan->ier |= UART_IER_MSI; sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); } static void sc16is7x2_break_ctl(struct uart_port *port, int break_state) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; unsigned long flags; dev_dbg(&ts->spi->dev, "%s\n", __func__); printk(KERN_DEBUG "---------> function call %s\n", __func__); spin_lock_irqsave(&chan->uart.lock, flags); if (break_state == -1) chan->lcr |= UART_LCR_SBC; else chan->lcr &= ~UART_LCR_SBC; spin_unlock_irqrestore(&chan->uart.lock, flags); sc16is7x2_write_async(ts->spi, UART_LCR, ch, chan->lcr); } static int sc16is7x2_startup(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; unsigned long flags; dev_dbg(&ts->spi->dev, "%s (line %d)\n", __func__, port->line); spin_lock_irqsave(&chan->uart.lock, flags); chan->lcr = UART_LCR_WLEN8; chan->mcr = __set_mctrl(chan->uart.mctrl); chan->fcr = 0; chan->ier = UART_IER_RLSI | UART_IER_RDI; spin_unlock_irqrestore(&chan->uart.lock, flags); { u8 tmp; sc16is7x2_write_async(ts->spi, UART_IER, ch,0); tmp = sc16is7x2_read(ts->spi, UART_IIR, ch); tmp = sc16is7x2_read(ts->spi, UART_LSR, ch); tmp = sc16is7x2_read(ts->spi, UART_MSR, ch); sc16is7x2_write_async(ts->spi, UART_FCR, ch,UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); sc16is7x2_write_async(ts->spi, UART_FCR, ch,chan->fcr); sc16is7x2_write_async(ts->spi, UART_LCR, ch,chan->lcr); sc16is7x2_write_async(ts->spi, UART_MCR, ch,chan->mcr); } chan->active = true; return 0; } static void sc16is7x2_shutdown(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned long flags; unsigned ch = port->line & 0x01; dev_dbg(&ts->spi->dev, "%s\n", __func__); BUG_ON(!chan); BUG_ON(!ts); if (ts->suspending) return; /* Disable interrupts from this port */ chan->ier = 0; chan->active = false; sc16is7x2_write(ts->spi, UART_IER, ch, chan->ier); /* Wait for worker of this channel to finish */ mutex_lock(&chan->lock); spin_lock_irqsave(&chan->uart.lock, flags); chan->mcr = __set_mctrl(chan->uart.mctrl); spin_unlock_irqrestore(&chan->uart.lock, flags); /* Disable break condition and FIFOs */ chan->lcr &= ~UART_LCR_SBC; sc16is7x2_write(ts->spi, UART_MCR, ch, chan->mcr); sc16is7x2_write(ts->spi, UART_LCR, ch, chan->lcr); mutex_unlock(&chan->lock); } static void sc16is7x2_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; unsigned ch = port->line & 0x01; unsigned long flags; unsigned int baud, quot; u8 ier, mcr, lcr, fcr = 0; u8 efr = UART_EFR_ECB; printk(KERN_DEBUG "---------> function call %s\n", __func__); /* set word length */ switch (termios->c_cflag & CSIZE) { case CS5: lcr = UART_LCR_WLEN5; break; case CS6: lcr = UART_LCR_WLEN6; break; case CS7: lcr = UART_LCR_WLEN7; break; default: case CS8: lcr = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) lcr |= UART_LCR_STOP; if (termios->c_cflag & PARENB) lcr |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) lcr |= UART_LCR_EPAR; #ifdef CMSPAR if (termios->c_cflag & CMSPAR) lcr |= UART_LCR_SPAR; #endif /* Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, port->uartclk / 16); quot = uart_get_divisor(port, baud); dev_dbg(&ts->spi->dev, "%s (baud %u)\n", __func__, baud); /* configure the fifo */ if (baud < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00; else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01; /* * MCR-based auto flow control. When AFE is enabled, RTS will be * deasserted when the receive FIFO contains more characters than * the trigger, or the MCR RTS bit is cleared. In the case where * the remote UART is not using CTS auto flow control, we must * have sufficient FIFO entries for the latency of the remote * UART to respond. IOW, at least 32 bytes of FIFO. */ chan->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) chan->mcr |= UART_MCR_AFE; /* * Ok, we're now changing the port state. Do it with * interrupts disabled. */ spin_lock_irqsave(&chan->uart.lock, flags); /* we are sending char from a workqueue so enable */ // chan->uart.state->port.tty->low_latency = 1; chan->uart.info->port.tty->low_latency = 1; /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); chan->uart.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) chan->uart.read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (termios->c_iflag & (BRKINT | PARMRK)) chan->uart.read_status_mask |= UART_LSR_BI; /* Characters to ignore */ chan->uart.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) chan->uart.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (termios->c_iflag & IGNBRK) { chan->uart.ignore_status_mask |= UART_LSR_BI; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) chan->uart.ignore_status_mask |= UART_LSR_OE; } /* ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) chan->uart.ignore_status_mask |= UART_LSR_DR; /* CTS flow control flag and modem status interrupts */ chan->ier &= ~UART_IER_MSI; if (UART_ENABLE_MS(&chan->uart, termios->c_cflag)) chan->ier |= UART_IER_MSI; if (termios->c_cflag & CRTSCTS) efr |= UART_EFR_CTS | UART_EFR_RTS; mcr = __set_mctrl(chan->uart.mctrl); ier = chan->ier; chan->lcr = lcr; /* Save LCR */ chan->fcr = fcr; /* Save FCR */ chan->mcr = mcr; /* Save MCR */ fcr |= UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; spin_unlock_irqrestore(&chan->uart.lock, flags); sc16is7x2_write_async(ts->spi, UART_LCR, ch,UART_LCR_DLAB); printk(KERN_DEBUG "+++++++++++++++++++> %s £º%d after write to read UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch)); sc16is7x2_write_async(ts->spi, UART_DLL, ch,quot & 0xff); sc16is7x2_write_async(ts->spi, UART_DLM, ch,(quot >> 8) & 0xff); sc16is7x2_write_async(ts->spi, UART_LCR, ch,0xBF); sc16is7x2_write_async(ts->spi, UART_EFR, ch, efr); sc16is7x2_write_async(ts->spi, UART_LCR, ch, lcr); sc16is7x2_write_async(ts->spi, UART_FCR, ch, fcr); sc16is7x2_write_async(ts->spi, UART_MCR, ch, mcr); sc16is7x2_write_async(ts->spi, UART_IER, ch, ier); #if 0 //for test { printk(KERN_DEBUG "+++++++++++++++++++> %s £º%d termios UART_LCR = %x lcr = %x\n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch),lcr); printk(KERN_DEBUG "+++++++++++++++++++> %s £º%d termios UART_LCR = %x lcr = %x\n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch),lcr); sc16is7x2_write(ts->spi, UART_LCR, ch,UART_LCR_DLAB); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_DLL = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_DLL,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_DLM = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_DLM,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_MCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_MCR,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_IER = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_IER,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_IIR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_IIR,ch)); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_LSR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LSR,ch)); sc16is7x2_write(ts->spi, UART_LCR, ch,lcr); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d after restore UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR,ch)); } #endif /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); } static const char * sc16is7x2_type(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); return TYPE_NAME; } static void sc16is7x2_release_port(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); ts->force_end_work = 1; } static int sc16is7x2_request_port(struct uart_port *port) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); return 0; } static void sc16is7x2_config_port(struct uart_port *port, int flags) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); if (flags & UART_CONFIG_TYPE) chan->uart.type = PORT_SC16IS7X2; } static int sc16is7x2_verify_port(struct uart_port *port, struct serial_struct *ser) { struct sc16is7x2_channel *chan = container_of(port, struct sc16is7x2_channel, uart); struct sc16is7x2_chip *ts = chan->chip; dev_dbg(&ts->spi->dev, "%s\n", __func__); if (ser->irq < 0 || ser->baud_base < 9600 || ser->type != PORT_SC16IS7X2) return -EINVAL; return 0; } static struct uart_ops sc16is7x2_uart_ops = { .tx_empty = sc16is7x2_tx_empty, .set_mctrl = sc16is7x2_set_mctrl, //open»áµ÷ÓÃ????? .get_mctrl = sc16is7x2_get_mctrl, .stop_tx = sc16is7x2_stop_tx, .start_tx = sc16is7x2_start_tx, .stop_rx = sc16is7x2_stop_rx, .enable_ms = sc16is7x2_enable_ms, .break_ctl = sc16is7x2_break_ctl, .startup = sc16is7x2_startup, //open µÄÈë¿Ú .shutdown = sc16is7x2_shutdown, .set_termios = sc16is7x2_set_termios, //open»áµ÷Óà tcgetattr .type = sc16is7x2_type, .release_port = sc16is7x2_release_port, .request_port = sc16is7x2_request_port, .config_port = sc16is7x2_config_port, .verify_port = sc16is7x2_verify_port, }; #define MIN(a, b) ((a < b) ? (a) : (b)) /* ******************************** IRQ ********************************* */ static void sc16is7x2_handle_fifo_rx(struct sc16is7x2_channel *chan) { struct uart_port *uart = &chan->uart; //struct tty_struct *tty = uart->state->port.tty; struct tty_struct *tty = uart->info->port.tty; u8 *rxbuf = chan->rxbuf; u8 lsr = chan->lsr; unsigned i, count = chan->fifo_rx.len; unsigned long flags; char flag = TTY_NORMAL; printk(KERN_DEBUG "---------> function call %s\n", __func__); spin_lock_irqsave(&uart->lock, flags); if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { /* * For statistics only */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); chan->uart.icount.brk++; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ if (uart_handle_break(&chan->uart)) goto ignore_char; } else if (lsr & UART_LSR_PE) chan->uart.icount.parity++; else if (lsr & UART_LSR_FE) chan->uart.icount.frame++; if (lsr & UART_LSR_OE) chan->uart.icount.overrun++; /* * Mask off conditions which should be ignored. */ lsr &= chan->uart.read_status_mask; if (lsr & UART_LSR_BI) flag = TTY_BREAK; else if (lsr & UART_LSR_PE) flag = TTY_PARITY; else if (lsr & UART_LSR_FE) flag = TTY_FRAME; } for (i = 1; i < count; i++) { uart->icount.rx++; if (!uart_handle_sysrq_char(uart, rxbuf[i])) uart_insert_char(uart, lsr, UART_LSR_OE, rxbuf[i], flag); } ignore_char: spin_unlock_irqrestore(&uart->lock, flags); if (count > 1) tty_flip_buffer_push(tty); } static void sc16is7x2_handle_fifo_tx(struct sc16is7x2_channel *chan) { struct uart_port *uart = &chan->uart; //struct circ_buf *xmit = &uart->state->xmit; struct circ_buf *xmit = &uart->info->xmit; unsigned count = chan->fifo_tx[1].len + chan->fifo_tx[2].len; unsigned long flags; BUG_ON(!uart); BUG_ON(!xmit); spin_lock_irqsave(&uart->lock, flags); uart->icount.tx += count; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(uart); if (uart_circ_empty(xmit)) { __stop_tx(chan); } spin_unlock_irqrestore(&uart->lock, flags); } static bool sc16is7x2_msg_add_fifo_rx(struct sc16is7x2_chip *ts, unsigned ch) { struct spi_message *m = &(ts->fifo_message); struct spi_transfer *t = &(ts->channel[ch].fifo_rx); int rxlvl = sc16is7x2_read(ts->spi, REG_RXLVL, ch); if (rxlvl > 0) { t->len = rxlvl + 1; spi_message_add_tail(t, m); return true; } return false; } static bool sc16is7x2_msg_add_fifo_tx(struct sc16is7x2_chip *ts, unsigned ch) { struct sc16is7x2_channel * const chan = &(ts->channel[ch]); struct uart_port *uart = &chan->uart; //struct circ_buf *xmit = &uart->state->xmit; struct circ_buf *xmit = &uart->info->xmit; unsigned count; bool split_transfer; u8 txlvl; printk(KERN_DEBUG "---------> function call %s\n", __func__); if (chan->uart.x_char && chan->lsr & UART_LSR_THRE) { dev_dbg(&ts->spi->dev, "tx: x-char\n"); sc16is7x2_write(ts->spi, UART_TX, ch, uart->x_char); uart->icount.tx++; uart->x_char = 0; return false; } if (uart_tx_stopped(&chan->uart)) { dev_dbg(&ts->spi->dev, "tx: stopped!\n"); sc16is7x2_stop_tx(uart); return false; } if (uart_circ_empty(xmit)) { __stop_tx(chan); return false; } txlvl = sc16is7x2_read(ts->spi, REG_TXLVL, ch); if (txlvl <= 0) { dev_dbg(&ts->spi->dev, " fifo full\n"); return false; } /* number of bytes to transfer to the fifo */ count = MIN(txlvl, uart_circ_chars_pending(xmit)); //uart_circ_chars_pending = ((head) - (tail)) & ((UART_XMIT_SIZE)-1) printk(KERN_DEBUG "+++++++++>kathy test%s:%d channel = %d bytes to transfer to the fifo = %d \n",__func__,__LINE__,ch,count); split_transfer = (UART_XMIT_SIZE - xmit->tail) <= count; /* add command transfer */ spi_message_add_tail(&(chan->fifo_tx[0]), &(ts->fifo_message)); /* add first fifo transfer */ spi_message_add_tail(&(chan->fifo_tx[1]), &(ts->fifo_message)); chan->fifo_tx[1].tx_buf = xmit->buf + xmit->tail; if (!split_transfer) { chan->fifo_tx[1].len = count; //chan->fifo_tx[1].cs_change = true; chan->fifo_tx[1].cs_change = false; chan->fifo_tx[2].len = 0; } else { chan->fifo_tx[1].len = (UART_XMIT_SIZE - 1) - xmit->tail; // chan->fifo_tx[1].cs_change = false; chan->fifo_tx[1].cs_change = true; chan->fifo_tx[2].tx_buf = xmit->buf; chan->fifo_tx[2].cs_change = true; // chan->fifo_tx[2].cs_change = false; chan->fifo_tx[2].len = count - chan->fifo_tx[1].len; /* add second fifo transfer */ spi_message_add_tail(&(chan->fifo_tx[2]), &(ts->fifo_message)); } xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); return true; } #if 0 //kathy chang delet 2010-11-22 10:14 static void sc16is7x2_handle_modem(struct sc16is7x2_chip *ts, unsigned ch) { struct sc16is7x2_channel *chan = &(ts->channel[ch]); struct uart_port *uart = &chan->uart; if (chan->msr & UART_MSR_ANY_DELTA && chan->ier & UART_IER_MSI && uart->state != NULL) { if (chan->msr & UART_MSR_TERI) uart->icount.rng++; if (chan->msr & UART_MSR_DDSR) uart->icount.dsr++; if (chan->msr & UART_MSR_DDCD) uart_handle_dcd_change(uart, chan->msr & UART_MSR_DCD); if (chan->msr & UART_MSR_DCTS) uart_handle_cts_change(uart, chan->msr & UART_MSR_CTS); wake_up_interruptible(&uart->state->port.delta_msr_wait); } } #endif static bool sc16is7x2_handle_channel(struct sc16is7x2_chip *ts, unsigned ch) { struct sc16is7x2_channel *chan = &(ts->channel[ch]); struct spi_message *m = &(ts->fifo_message); bool rx, tx; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s (%i)\n", __func__, ch); chan->iir = sc16is7x2_read(ts->spi, UART_IIR, ch); chan->msr = sc16is7x2_read(ts->spi, UART_MSR, ch); chan->lsr = sc16is7x2_read(ts->spi, UART_LSR, ch); #if 0 //kathy chang delete 2010-11-22 10:13 sc16is7x2_handle_modem(ts, ch); #endif spi_message_init(m); rx = sc16is7x2_msg_add_fifo_rx(ts, ch); tx = sc16is7x2_msg_add_fifo_tx(ts, ch); printk(KERN_DEBUG "+++++++++>kathy test%s:%d rx %d tx =%d\n",__func__,__LINE__,rx,tx); if (rx || tx) spi_sync(ts->spi, m); if (rx) sc16is7x2_handle_fifo_rx(chan); if (tx) sc16is7x2_handle_fifo_tx(chan); dev_dbg(&ts->spi->dev, "%s finished (iir = 0x%02x)\n", __func__, chan->iir); return (chan->iir & UART_IIR_NO_INT) == 0x00; // } static void sc16is7x2_work(struct work_struct *w) { struct sc16is7x2_chip *ts = container_of(w, struct sc16is7x2_chip, work); unsigned pending = 0; unsigned ch = 0; printk(KERN_DEBUG "---------> function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); BUG_ON(!w); BUG_ON(!ts); if (ts->force_end_work) { dev_dbg(&ts->spi->dev, "%s: force end!\n", __func__); return; } if (ts->channel[0].active) pending |= BIT(0); if (ts->channel[1].active) pending |= BIT(1); do { mutex_lock(&(ts->channel[ch].lock)); if (pending & BIT(ch) && ts->channel[ch].active) { if (!sc16is7x2_handle_channel(ts, ch)) pending &= ~BIT(ch); } mutex_unlock(&(ts->channel[ch].lock)); ch ^= 1; /* switch channel */ } while (!ts->force_end_work && !freezing(current) && pending); dev_dbg(&ts->spi->dev, "%s finished\n", __func__); } static irqreturn_t sc16is7x2_interrupt(int irq, void *dev_id) { struct sc16is7x2_chip *ts = dev_id; printk(KERN_DEBUG "---------> sc16is7x2_interrupt function call %s\n", __func__); dev_dbg(&ts->spi->dev, "%s\n", __func__); if (!ts->force_end_work && !work_pending(&ts->work) && !freezing(current) && !ts->suspending) { queue_work(ts->workqueue, &ts->work); } return IRQ_HANDLED; } /* ******************************** INIT ********************************* */ static struct uart_driver sc16is7x2_uart_driver; static int sc16is7x2_register_uart_port(struct sc16is7x2_chip *ts, struct sc16is7x2_platform_data *pdata, unsigned ch) { struct sc16is7x2_channel *chan = &(ts->channel[ch]); struct uart_port *uart = &chan->uart; mutex_init(&chan->lock); chan->active = false; /* will be set in startup */ chan->chip = ts; chan->read_fifo_cmd = READ_CMD(UART_RX, ch); chan->fifo_rx.tx_buf = &(chan->read_fifo_cmd); chan->fifo_rx.rx_buf = chan->rxbuf; // chan->fifo_rx.cs_change = true; chan->fifo_rx.cs_change = false; chan->write_fifo_cmd = WRITE_CMD(UART_TX, ch); chan->fifo_tx[0].tx_buf = &(chan->write_fifo_cmd); chan->fifo_tx[0].rx_buf = NULL; chan->fifo_tx[0].len = 1; chan->fifo_tx[0].cs_change = false; chan->fifo_tx[1].rx_buf = NULL; chan->fifo_tx[2].rx_buf = NULL; uart->irq = ts->spi->irq; uart->uartclk = pdata->uartclk; uart->fifosize = FIFO_SIZE; uart->ops = &sc16is7x2_uart_ops; uart->flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; uart->line = pdata->uart_base + ch; uart->type = PORT_SC16IS7X2; uart->dev = &ts->spi->dev; return uart_add_one_port(&sc16is7x2_uart_driver, uart); } static int __devinit sc16is7x2_probe(struct spi_device *spi) { struct sc16is7x2_chip *ts; struct sc16is7x2_platform_data *pdata; int ret; printk(KERN_DEBUG "---------> function call %s\n", __func__); spi->max_speed_hz = H_SPI_SPEED; //can modify pdata = &sc16is7x2_spi_data; ret = spi_setup(spi); if (ret < 0) return ret; ts = kzalloc(sizeof(struct sc16is7x2_chip), GFP_KERNEL); if (!ts) return -ENOMEM; mutex_init(&ts->lock); dev_set_drvdata(&spi->dev, ts); ts->spi = spi; ts->force_end_work = 1; /* Reset the chip TODO: and disable IRQ output */ sc16is7x2_write(spi, REG_IOC, 0, IOC_SRESET); at91_sys_write(AT91_PMC+PMC_PCER,1<<spi->irq); at91_set_gpio_input(AT91_PIN_PC12, 1); at91_set_A_periph(AT91_PIN_PC12, 1); at91_sys_write(AT91_AIC_SMR(spi->irq),(1<<5)|7); at91_sys_write(AT91_AIC_ICCR,1<<spi->irq); printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d AT91_AIC_SMR(29) %x \n",__func__,__LINE__,at91_sys_read(AT91_AIC_SMR(spi->irq))); ret = request_irq(spi->irq, sc16is7x2_interrupt,0, "sc16is7x2", ts); if (ret) { printk(KERN_INFO "ERROR: can't get assigned irq %d \n\r", spi->irq); } if (ret) { dev_warn(&ts->spi->dev, "cannot register interrupt\n"); goto exit_destroy; } ret = sc16is7x2_register_uart_port(ts, pdata, 0); if (ret) goto exit_irq; ret = sc16is7x2_register_uart_port(ts, pdata, 1); if (ret) goto exit_uart0; ts->workqueue = create_freezeable_workqueue(DRIVER_NAME); if (!ts->workqueue) { dev_warn(&ts->spi->dev, "cannot create workqueue\n"); ret = -EBUSY; goto exit_uart1; } INIT_WORK(&ts->work, sc16is7x2_work); ts->force_end_work = 0; printk(KERN_INFO DRIVER_NAME " at CS%d (irq %d), 2 UARTs ttySC%d, ttySC%d is successful!!!!\n", spi->chip_select, spi->irq,pdata->uart_base, pdata->uart_base + 1); return ret; exit_uart1: uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart); exit_uart0: uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart); exit_irq: free_irq(spi->irq, ts); exit_destroy: dev_set_drvdata(&spi->dev, NULL); mutex_destroy(&ts->lock); kfree(ts); return ret; } static int __devexit sc16is7x2_remove(struct spi_device *spi) { struct sc16is7x2_chip *ts; int ret; ts = dev_get_drvdata(&spi->dev); if (ts == NULL) return -ENODEV; free_irq(spi->irq, ts); ts->force_end_work = 1; if (ts->workqueue) { flush_workqueue(ts->workqueue); destroy_workqueue(ts->workqueue); ts->workqueue = NULL; } dev_set_drvdata(&spi->dev, NULL); ret = uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart); if (ret) { dev_err(&spi->dev, "Failed to remove the UART port 0: %d\n", ret); goto exit_error; } ret = uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart); if (ret) { dev_err(&spi->dev, "Failed to remove the UART port 1: %d\n", ret); goto exit_error; } mutex_destroy(&ts->lock); kfree(ts); exit_error: return ret; } static struct uart_driver sc16is7x2_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = "ttySC", .major = SC16IS7X2_MAJOR, .minor = SC16IS7X2_MINOR, .nr = MAX_SC16IS7X2, }; static struct spi_driver sc16is7x2_spi_driver = { .driver = { .name = DRIVER_NAME, .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = sc16is7x2_probe, .remove = __devexit_p(sc16is7x2_remove), }; static int __init sc16is7x2_init(void) { int ret; ret = uart_register_driver(&sc16is7x2_uart_driver); if (ret) { printk(KERN_ERR "Couldn't register sc16is7x2 uart driver\n"); return ret; } return spi_register_driver(&sc16is7x2_spi_driver); } module_init(sc16is7x2_init); static void __exit sc16is7x2_exit(void) { spi_unregister_driver(&sc16is7x2_spi_driver); uart_unregister_driver(&sc16is7x2_uart_driver); } module_exit(sc16is7x2_exit); MODULE_AUTHOR("kathy chang"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SC16IS7x2 SPI based UART chip"); MODULE_ALIAS("spi:" DRIVER_NAME);
begin:vcard fn:Manuel Stahl n:Stahl;Manuel email;internet:manuel.stahl@xxxxxxxxxxxxxxxxx tel;work:+49 911 58061-6419 x-mozilla-html:FALSE version:2.1 end:vcard