/****************************************************************************** ** ** FILE NAME : ifxmips_ssc.c ** PROJECT : IFX UEIP ** MODULES : SSC (Synchronous Serial Controller) ** ** DATE : 3 July 2009 ** AUTHOR : Lei Chuanhua ** DESCRIPTION : SCC Driver ** COPYRIGHT : Copyright (c) 2009 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** 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; either version 2 of the License, or ** (at your option) any later version. ** ** HISTORY ** $Date $Author $Comment ** 3 Jul,2009 Lei Chuanhua First UEIP release *******************************************************************************/ /*! \file ifxmips_ssc.c \ingroup IFX_SSC \brief ssc bus driver source file */ #ifndef EXPORT_SYMTAB #define EXPORT_SYMTAB #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Project header */ #include #include #include #include #include #include #include #include "ifxmips_ssc_reg.h" #include "ifxmips_ssc.h" #ifdef CONFIG_IFX_PMCU #include "ifxmips_ssc_pm.h" #endif /* CONFIG_IFX_PMCU */ #include #define IFX_SSC_VER_MAJOR 2 #define IFX_SSC_VER_MID 4 #define IFX_SSC_VER_MINOR 1 #define IFX_SSC_SPLIT_BAUD_RATE 25000000 #ifdef CONFIG_IFX_SPI_DEBUG #define INLINE enum { SSC_MSG_TX_FIFO = 0x00000001, SSC_MSG_TX_DMA = 0x00000002, SSC_MSG_RX_FIFO = 0x00000004, SSC_MSG_RX_DMA = 0x00000008, SSC_MSG_INT = 0x00000010, /* Interrupt msg */ SSC_MSG_CFG = 0x00000020, SSC_MSG_THREAD = 0x00000040, SSC_MSG_TASKLET = 0x00000080, SSC_MSG_DEBUG = 0x00000100, SSC_MSG_ERROR = 0x00000200, SSC_MSG_INIT = 0x00000400, /* Initialization msg */ SSC_MSG_QUEUE = 0x00000800, SSC_MSG_LOCK = 0x00001000, SSC_MSG_CALLBACK = 0x00002000, SSC_MSG_DUPLEX = 0x00004000, /* duplex mode */ SSC_MSG_ANY = 0xffffffff, /* anything */ }; static void ifx_ssc_debug(struct ifx_ssc_port *port, const char *fmt, ...); static void ifx_ssc_debug_port_chains(struct ifx_ssc_port *port); static void ifx_ssc_dump_register(void); static void _ifx_ssc_debug_ssc_command(const char *prefix, unsigned char cmd); static void _ifx_ssc_show_buf(const char *prefix, const char *name, char *buf, size_t len); static int ifx_ssc_block_until_write_is_complete(struct ssc_device *dev); static void ifx_ssc_cs_lock(ssc_device_t *dev); static INLINE void ifx_ssc_cs_unlock(ssc_device_t *dev); u32 ifx_ssc_debug_fifo_op = 0; u32 ifx_ssc_debug_block_after_write_op = 1; #define IFX_SSC_PRINT(_port, _m, _fmt, args...) do { \ if ((_port)->ssc_debug & (_m)) { \ ifx_ssc_debug((_port), (_fmt), ##args); \ } \ } while (0) #else #define INLINE inline #define IFX_SSC_PRINT(_port, _m, _fmt, ...) #endif /* CONFIG_IFX_SPI_DEBUG */ #define IFX_SSC_NAME "ifx_ssc" /* This irq number is platform specific, defined in platform header.h */ #define IFX_SSC_FAKE_IRQ_NO IFX_SSC_TIR /** * This is the per-channel data structure containing pointers, flags * and variables for the port. ifx_ssc_isp is allocated in ifx_ssc_init() * based on the chip version. */ static struct ifx_ssc_port *ifx_ssc_isp = NULL; static struct proc_dir_entry *ifx_ssc_proc; typedef struct { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) int (*request) (unsigned int irq, irqreturn_t (*handler) (int, void *), unsigned long irqflags, const char *devname, void *dev_id); #else int (*request) (unsigned int irq, irqreturn_t (*handler) (int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id); #endif void (*free) (unsigned int irq, void *dev_id); } ifx_ssc_int_wrapper_t; struct chain_queue_head { struct ssc_device *dev; struct list_head *current_qelem; struct list_head list; TAILQ_ENTRY(chain_queue_head) queue_hook; }; static ifx_ssc_int_wrapper_t ifx_ssc_int_wrapper = { .request = request_irq, .free = free_irq, }; /** * \fn static INLINE void ifx_ssc_raise_fake_irq(struct ifx_ssc_port *port, unsigned int irq) * \brief Set ICU request register bit to generate fake interrupt * * \param port Pointer to structure #ifx_ssc_port * \param irq Fake interrupt irq number * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE void ifx_ssc_raise_fake_irq(struct ifx_ssc_port *port, unsigned int irq) { unsigned long flags; IFX_SSC_PRINT(port, SSC_MSG_INT, "%s irq %d triggered \n", __func__, irq); spin_lock_irqsave(&port->ssc_irq_lock, flags); ifx_icu_irsr_set(irq); spin_unlock_irqrestore(&port->ssc_irq_lock, flags); } /** * \fn static void ifx_ssc_start_tasklet(struct ifx_ssc_port *port) * \brief Trigger different schedule procedures according to different context. * if caller is already in tasklet, it will be done in caller's tasklet * * \param port Pointer to structure #ifx_ssc_port * \return none * \ingroup IFX_SSC_INTERNAL */ static void ifx_ssc_start_tasklet(struct ifx_ssc_port *port) { struct tasklet_struct *ptasklet; /* * Calls the internal process to serve the queue. This routine would * immediately return in case the SSC hardware is currently used to serve * another request. */ ptasklet = &port->ssc_txrxq; if (in_irq()) { /* Hardware irq */ IFX_SSC_PRINT(port, SSC_MSG_INT, "%s hardware irq schedule\n", __func__); tasklet_hi_schedule(ptasklet); } else if (in_softirq()) { /* Softirq or tasklet */ IFX_SSC_PRINT(port, SSC_MSG_TASKLET, "%s softirq schedule\n", __func__); if (tasklet_trylock(ptasklet)) { /* tasklet_trylock for SMP*/ ptasklet->func(ptasklet->data); tasklet_unlock(ptasklet); } else { IFX_SSC_PRINT(port, SSC_MSG_TASKLET, "%s should never happen\n", __func__); } } else { /* Process context */ IFX_SSC_PRINT(port, SSC_MSG_THREAD, "%s process schedule\n", __func__); ifx_ssc_raise_fake_irq(port, port->ssc_fake_irq); } } /** * \fn static irqreturn_t ifx_ssc_fake_isr (int irq, void *dev_id) * \brief Fake interrupt handler * * \param irq fake interrupt irq number * \param dev_id vague type, will be converted to * pointer to structure #ifx_ssc_port * \return IRQ_HANDLED irq has been handled. * \ingroup IFX_SSC_INTERNAL */ static irqreturn_t #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) ifx_ssc_fake_isr (int irq, void *dev_id) #else ifx_ssc_fake_isr (int irq, void *dev_id, struct pt_regs *regs) #endif { struct ifx_ssc_port *port = (struct ifx_ssc_port *) dev_id; IFX_SSC_PRINT(port, SSC_MSG_INT, "%s irq %d served\n", __func__, irq); ifx_icu_irsr_clr(irq); ifx_ssc_start_tasklet(port); return IRQ_HANDLED; } #ifdef CONFIG_IFX_SPI_DEBUG /** * \fn static void ifx_ssc_debug(struct ifx_ssc_port *port, const char *fmt, ...) * \brief Debug all kinds of level message * * \param port Pointer to structure #ifx_ssc_port * \param fmt debug output format * * \return none * \ingroup IFX_SSC_INTERNAL */ static void ifx_ssc_debug(struct ifx_ssc_port *port, const char *fmt, ...) { static char buf[256] = {0}; /* XXX */ va_list ap; static const char *p = "kthread"; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); printk("%s %s: %s\n", p, port->name, buf); } #endif /* CONFIG_IFX_SPI_DEBUG */ /** * \fn static INLINE void ifx_ssc_wait_finished(struct ifx_ssc_port *port) * \brief Wait for SPI bus becomes idle, FIFO empty doesn't means spi bus idle. * to start another transaction, need to make sure bus is idle * * \param port Pointer to structure #ifx_ssc_port * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE void ifx_ssc_wait_finished(struct ifx_ssc_port *port) { unsigned int cntdown = 1000 * 1000; while((IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_BUSY)){ udelay(1) ; /* Do nothing */ if(cntdown-- == 0) { /*--- ifx_ssc_dump_register(); ---*/ printk(KERN_ERR"%s: in BUSY_STATE\n", __func__); break; } } } /** * \fn static INLINE unsigned int ifx_ssc_get_kernel_clk(struct ifx_ssc_port *port) * \brief Get SSC clock speed. * Returns the current operating speed of the SSC peripheral, depending on chip * specific bus speed and RMC setting in CLC register. * * \param port Pointer to structure #ifx_ssc_port * \return >0 Peripheral speed in HZ * \return 0 Error * \ingroup IFX_SSC_INTERNAL */ static INLINE unsigned int ifx_ssc_get_kernel_clk(struct ifx_ssc_port *port) { /* This function assumes that the CLC register is set with the * appropriate value for RMC. */ unsigned int rmc; rmc = IFX_SSC_GET_CLC_RMC(port); if (rmc == 0) { printk(KERN_ERR "%s rmc==0 \n", __func__); return 0; } return (ifx_get_fpi_hz() / rmc); } static ssize_t ifx_ssc_proc_stats_show(struct seq_file *m, void *v __maybe_unused) { int port_nr = 0; ssc_device_t *dev; seq_puts(m, "Statistics for IFX Synchronous Serial Controller (SSC)\n\n"); for (port_nr = 0; port_nr < IFX_SSC_MAX_PORT_NUM; port_nr++) { struct ifx_ssc_port *port = &ifx_ssc_isp[port_nr]; struct ifx_ssc_statistics *stats = &port->stats; seq_printf(m, "SSC%d\n", port->port_idx); seq_printf(m, "RX overflow errors %d\n", stats->rxOvErr); seq_printf(m, "RX underflow errors %d\n", stats->rxUnErr); seq_printf(m, "TX overflow errors %d\n", stats->txOvErr); seq_printf(m, "TX underflow errors %d\n", stats->txUnErr); seq_printf(m, "Abort errors %d\n", stats->abortErr); seq_printf(m, "Mode errors %d\n", stats->modeErr); seq_printf(m, "RX Bytes %llu\n", stats->rxBytes); seq_printf(m, "TX Bytes %llu\n", stats->txBytes); seq_printf(m, "TX FIFO transaction %llu\n", stats->txFifo); seq_printf(m, "TX DMA transaction %llu\n", stats->txDma); seq_printf(m, "TX DMA bytes %llu\n", stats->txDmaBytes); seq_printf(m, "RX FIFO transaction %llu\n", stats->rxFifo); seq_printf(m, "RX DMA transaction %llu\n", stats->rxDma); seq_printf(m, "RX DMA bytes %llu\n", stats->rxDmaBytes); seq_printf(m, "SSC bus status %s\n", port->ssc_cs_locked ? "locked" : "unlocked"); seq_puts(m, "\n"); /* Per device statistics */ IFX_SSC_SEM_LOCK(port->dev_sem); TAILQ_FOREACH(dev, &port->ssc_devq, dev_entry) { seq_printf(m, "Device %s duplex %s:\n", dev->dev_name, (dev->duplex == IFX_SSC_HALF_DUPLEX) ? "Half" : "Full"); seq_printf(m, "Rx Bytes %llu\n", dev->stats.rxBytes); seq_printf(m, "Tx Bytes %llu\n", dev->stats.txBytes); seq_printf(m, "Context errors %d\n", dev->stats.context_err); seq_printf(m, "Duplicated qentry errors %d\n", dev->stats.dup_qentries); seq_printf(m, "Fragment errors %d\n", dev->stats.frag_err); seq_printf(m, "Handler errors %d\n", dev->stats.handler_err); seq_printf(m, "Duplex errors %d\n", dev->stats.dlx_err); seq_printf(m, "Enqueue %llu\n", dev->stats.enqueue); seq_printf(m, "Dequeue %llu\n", dev->stats.dequeue); } IFX_SSC_SEM_UNLOCK(port->dev_sem); } return 0; } static int ifx_ssc_proc_stats_open(struct inode *inode __maybe_unused, struct file *file) { return single_open(file, ifx_ssc_proc_stats_show, NULL); } static int _dump_reg(struct seq_file *m, char *buf, int port_nr) { #define IFX_SSC_REG_MAX 20 u32 stats[IFX_SSC_REG_MAX] = {0}; #undef IFX_SSC_REG_MAX struct ifx_ssc_port *port; int ret; int (*show)(struct seq_file *, const char *fmt, ...); BUG_ON(m && buf); show = m ? seq_printf : (typeof(show))sprintf; if (port_nr >= IFX_SSC_MAX_PORT_NUM) { show(m, ""); return -ENOENT; } port = &ifx_ssc_isp[port_nr]; IFX_SSC_IRQ_LOCK(port); stats[0] = IFX_SSC_GET_CLC(port); stats[1] = IFX_SSC_GET_ID(port); stats[2] = IFX_SSC_GET_CON(port); stats[3] = IFX_SSC_GET_STATE(port); stats[4] = IFX_SSC_GET_TX_WORD(port); stats[5] = IFX_SSC_GET_FIFO_STATUS(port); stats[6] = IFX_SSC_GET_RX_FIFO_CTRL(port); stats[7] = IFX_SSC_GET_TX_FIFO_CTRL(port); stats[8] = IFX_SSC_GET_BR(port); stats[9] = IFX_SSC_GET_FRAMING_CON(port); stats[10] = IFX_SSC_GET_FRAMING_STATUS(port); stats[11] = IFX_SSC_GET_GPOCON(port); stats[12] = IFX_SSC_GET_GPOSTAT(port); stats[13] = IFX_SSC_GET_RXREQ(port); stats[14] = IFX_SSC_GET_RXCNT(port); stats[15] = IFX_SSC_GET_DMA_CON(port); stats[16] = IFX_SSC_GET_IRN_EN(port); stats[17] = IFX_SSC_GET_IRN_CR(port); stats[18] = IFX_SSC_GET_IRN_ICR(port); IFX_SSC_IRQ_UNLOCK(port); ret = show(m, "SSC%d\n" "IFX_SSC_CLC 0x%08x\n" "IFX_SSC_ID 0x%08x\n" "IFX_SSC_MCON 0x%08x\n" "IFX_SSC_STATE 0x%08x\n" "IFX_SSC_TB 0x%08x\n" "IFX_SSC_FSTAT 0x%08x\n" "IFX_SSC_RXFCON 0x%08x\n" "IFX_SSC_TXFCON 0x%08x\n" "IFX_SSC_BR 0x%08x\n" "IFX_SSC_SFCON 0x%08x\n" "IFX_SSC_SFSTAT 0x%08x\n" "IFX_SSC_GPOCON 0x%08x\n" "IFX_SSC_GPOSTAT 0x%08x\n" "IFX_SSC_RXREQ 0x%08x\n" "IFX_SSC_RXCNT 0x%08x\n" "IFX_SSC_DMACON 0x%08x\n" "IFX_SSC_IRN_EN 0x%08x\n" "IFX_SSC_IRN_CR 0x%08x\n" "IFX_SSC_IRN_ICR 0x%08x\n" "\n", port->port_idx, stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], stats[6], stats[7], stats[8], stats[9], stats[10], stats[11], stats[12], stats[13], stats[14], stats[15], stats[16], stats[17], stats[18]); return ret; } static ssize_t ifx_ssc_proc_reg_read_show(struct seq_file *m, void *v __maybe_unused) { int port_nr = 0; seq_puts(m, "Register Dump for IFX Synchronous Serial Controller" "(SSC)\n\n"); while (_dump_reg(m, NULL, port_nr++) >= 0) ; return 0; } static int ifx_ssc_proc_reg_read_open(struct inode *inode __maybe_unused, struct file *file) { return single_open(file, ifx_ssc_proc_reg_read_show, NULL); } static void ifx_ssc_dump_register(void) { char buf[768]; size_t off = 0; int ret = 0, port_nr = 0; while (ret >= 0) { ret = _dump_reg(NULL, &buf[off], port_nr++); if (ret < 0) break; off += ret; BUG_ON(off > sizeof(buf)); } pr_err("%s", buf); } /** * \fn static INLINE void ifx_ssc_gpio_init(void) * \brief Reserve and initialize GPIO for SSC . * * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE void ifx_ssc_gpio_init(void) { ifx_gpio_register(IFX_GPIO_MODULE_SSC); } /** * \fn static INLINE void ifx_ssc_gpio_release(void) * \brief Release reserverd gpio resource so that other module could use it. * \return none * * \ingroup IFX_SSC_INTERNAL */ static INLINE void ifx_ssc_gpio_release(void) { ifx_gpio_deregister(IFX_GPIO_MODULE_SSC); } /** * \fn static INLINE int ifx_ssc_rxtx_mode_set(struct ifx_ssc_port *port, unsigned int val) * \brief Rx/Tx mode set. * Set the transmission mode while SSC is idle * * \param port Pointer to structure #ifx_ssc_port * \param val Rx/Tx mode * \return 0 OK * \return -EINVAL Invalid parameters supplied * \return -EBUSY Transmission or reception ongoing * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_rxtx_mode_set(struct ifx_ssc_port *port, unsigned int val) { int retry; u32 reg; if (!(port) || (val & ~(IFX_SSC_MODE_MASK))) { return -EINVAL; } /* check BUSY and RXCNT */ retry = 1000; while ((IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_BUSY)) { pr_err_ratelimited("%s: state busy\n", __func__); if (!retry--) return -EBUSY; schedule(); } retry = 1000; while (IFX_SSC_RX_TO_RECEIVED(port)) { pr_err_ratelimited("%s: rx todo busy\n", __func__); if (!retry--) return -EBUSY; schedule(); } IFX_SSC_IRQ_LOCK(port); reg = IFX_SSC_GET_CON(port); reg &= ~(IFX_SSC_CON_RX_OFF | IFX_SSC_CON_TX_OFF); reg |= val; IFX_SSC_SET_CON(reg, port); port->opts.modeRxTx = val; IFX_SSC_IRQ_UNLOCK(port); return 0; } /** * \fn static int ifx_ssc_sethwopts(struct ifx_ssc_port *port) * \brief SSC set hardware options. * This routine intializes the SSC appropriately depending on slave/master and * full-/half-duplex mode. It assumes that the SSC is disabled and the fifo's * and buffers are flushed later on. * * \param port Pointer to structure #ifx_ssc_port * \return 0 OK * \return -EINVAL Invalid hardware options supplied * \ingroup IFX_SSC_INTERNAL */ static int ifx_ssc_sethwopts(struct ifx_ssc_port *port) { unsigned long bits; u32 reg; struct ifx_ssc_hwopts *opts = &port->opts; /* sanity checks */ if ((opts->dataWidth < IFX_SSC_MIN_DATA_WIDTH) || (opts->dataWidth > IFX_SSC_MAX_DATA_WIDTH)) { printk(KERN_ERR "%s: sanity check failed\n", __func__); return -EINVAL; } bits = IFX_SSC_ENCODE_DATA_WIDTH(opts->dataWidth); bits |= IFX_SSC_CON_ENABLE_BYTE_VALID; /* TXB+2, TXB+3 trigger FPI Addr */ if (opts->rxOvErrDetect) bits |= IFX_SSC_CON_RX_OFL_CHECK; if (opts->rxUndErrDetect) bits |= IFX_SSC_CON_RX_UFL_CHECK; if (opts->txOvErrDetect) bits |= IFX_SSC_CON_TX_OFL_CHECK; if (opts->txUndErrDetect) bits |= IFX_SSC_CON_TX_UFL_CHECK; if (opts->loopBack) bits |= IFX_SSC_CON_LOOPBACK_MODE; if (opts->echoMode) bits |= IFX_SSC_CON_ECHO_MODE_ON; if (opts->headingControl) bits |= IFX_SSC_CON_MSB_FIRST; if (opts->clockPhase) bits |= IFX_SSC_CON_PH; if (opts->clockPolarity) bits |= IFX_SSC_CON_PO; switch (opts->modeRxTx) { case IFX_SSC_MODE_TX: bits |= IFX_SSC_CON_RX_OFF; break; case IFX_SSC_MODE_RX: bits |= IFX_SSC_CON_TX_OFF; break; } IFX_SSC_IRQ_LOCK(port); IFX_SSC_SET_CON(bits, port); reg = (port->opts.gpoCs << IFX_SSC_GPOCON_ISCSB0_POS) | (port->opts.gpoInv << IFX_SSC_GPOCON_INVOUT0_POS); IFX_SSC_SET_GPOCON(reg, port); reg = port->opts.gpoCs << IFX_SSC_WHBGPOSTAT_SETOUT0_POS; IFX_SSC_SET_FGPO(reg, port); if (opts->masterSelect) { reg = IFX_SSC_WHBSTATE_MASTER_MODE; } else { reg = IFX_SSC_WHBSTATE_SLAVE_MODE; } IFX_SSC_SET_WHBSTATE(reg, port); port->tx_fifo_size_words = IFX_SSC_TX_FIFO_SIZE(port); port->tx_fifo_size_bytes = port->tx_fifo_size_words << 2; port->rx_fifo_size_words = IFX_SSC_RX_FIFO_SIZE(port); port->rx_fifo_size_bytes = port->rx_fifo_size_words << 2; IFX_SSC_IRQ_UNLOCK(port); /* set up the port pins */ ifx_ssc_gpio_init(); return 0; } /** * \fn int ifx_ssc_cs_low(u32 pin) * \brief Chip select enable. * This function sets the given chip select for SSC0 to low. * * \param pin Selected CS pin * \return 0 OK * \return -EINVAL Invalid GPIO pin provided * \ingroup IFX_SSC_FUNCTIONS */ int ifx_ssc_cs_low(u32 pin) { int ret; u32 reg; struct ifx_ssc_port *port = &ifx_ssc_isp[0]; IFX_SSC_IRQ_LOCK(port); if (pin > IFX_SSC_MAX_GPO_OUT) { ret = -EINVAL; } else { reg = (1 << (pin + IFX_SSC_WHBGPOSTAT_CLROUT0_POS)); IFX_SSC_SET_FGPO(reg, port); smp_wmb(); ret = 0; } IFX_SSC_IRQ_UNLOCK(port); return ret; } EXPORT_SYMBOL(ifx_ssc_cs_low); /** * \fn int ifx_ssc_cs_high(u32 pin) * \brief Chip select disable. * This function sets the given chip select for SSC0 to high. * * \param pin Selected CS pin * \return 0 OK * \return -EINVAL Invalid GPIO pin provided * \ingroup IFX_SSC_FUNCTIONS */ int ifx_ssc_cs_high(u32 pin) { int ret; u32 reg; struct ifx_ssc_port *port = &ifx_ssc_isp[0]; IFX_SSC_IRQ_LOCK(port); if (pin > IFX_SSC_MAX_GPO_OUT) { ret = -EINVAL; } else { udelay(1); /* XXX, at least half of cycle of baudrate delay is needed */ reg = 1 << (pin + IFX_SSC_WHBGPOSTAT_SETOUT0_POS); IFX_SSC_SET_FGPO(reg, port); smp_wmb(); ret = 0; } IFX_SSC_IRQ_UNLOCK(port); return ret; } EXPORT_SYMBOL(ifx_ssc_cs_high); /*--- #define SSC_TIMING_CHECK ---*/ #if defined(SSC_TIMING_CHECK) #define CLK_TO_USEC(a) ((a) / 250) #define CLK_TO_NSEC(a) ((a * 1000) / 250) struct _generic_stat { signed long cnt; signed long avg; signed long min; signed long max; } ssc_timestat_rx, ssc_timestat_tx, ssc_timestat_rxtx, ssc_cntstat_rx, ssc_cntstat_tx; static DEFINE_SPINLOCK(ssc_stat_lock); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void init_generic_stat(struct _generic_stat *pgstat) { pgstat->min = LONG_MAX; pgstat->max = LONG_MIN; pgstat->cnt = 0; pgstat->avg = 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void generic_stat(struct _generic_stat *pgstat, signed long val) { unsigned long flags; spin_lock_irqsave(&ssc_stat_lock, flags); if(pgstat->cnt == 0) { init_generic_stat(pgstat); } if(val > pgstat->max) pgstat->max = val; if(val < pgstat->min) pgstat->min = val; pgstat->avg += val; pgstat->cnt++; spin_unlock_irqrestore(&ssc_stat_lock, flags); } /*--------------------------------------------------------------------------------*\ * timebase: 0 unveraendert, 1: clk -> usec, 2: clk -> nsec * reset: Statistik ruecksetzen \*--------------------------------------------------------------------------------*/ void display_generic_stat(const char *prefix, struct _generic_stat *pgstat, unsigned int timebase, unsigned int reset) { unsigned long flags; struct _generic_stat gstat; signed long cnt; spin_lock_irqsave(&ssc_stat_lock, flags); cnt = pgstat->cnt; if(cnt == 0) { spin_unlock_irqrestore(&ssc_stat_lock, flags); return; } memcpy(&gstat,pgstat, sizeof(gstat)); if(reset) { pgstat->cnt = 0; } spin_unlock_irqrestore(&ssc_stat_lock, flags); switch(timebase) { case 0: printk("%s[%ld] min=%ld max=%ld avg=%ld\n", prefix, cnt, gstat.min, gstat.max, gstat.avg / cnt); break; case 1: printk("%s[%ld] min=%ld max=%ld avg=%ld usec\n", prefix, cnt, CLK_TO_USEC(gstat.min), CLK_TO_USEC(gstat.max), CLK_TO_USEC(gstat.avg / cnt)); break; case 2: printk("%s[%ld] min=%ld max=%ld avg=%ld nsec\n", prefix, cnt, CLK_TO_NSEC(gstat.min), CLK_TO_NSEC(gstat.max), CLK_TO_NSEC(gstat.avg / cnt)); break; } } #endif/*--- #else ---*//*--- #if defined(SSC_TIMING_CHECK) ---*/ static void ifxssc_reset_rx_fifo(struct ifx_ssc_port *port) { unsigned int reg; IFX_SSC_IRQ_LOCK(port); printk("%s: rx-error occured, flush rx-fifo\n", __func__); reg = IFX_SSC_XFCON_FIFO_FLUSH | IFX_SSC_XFCON_FIFO_ENABLE | SM((port->rx_fifo_size_words >> 1), IFX_SSC_XFCON_ITL); IFX_SSC_RX_FIFO_CTRL(reg, port); IFX_SSC_SET_WHBSTATE(IFX_SSC_WHBSTATE_CLR_RX_OFL_ERROR , port); IFX_SSC_IRQ_UNLOCK(port); } /** * \fn static int ifx_ssc_set_baudrate (struct ifx_ssc_port *port, unsigned int baudrate) * \brief SSC set baudrate. * Sets the baudrate of the corresponding port according to the passed * rate after reading out the current module speed. * * \param port Pointer to structure #ifx_ssc_port * \param baudrate Desired baudrate * \return 0 OK * \return -EINVAL Could not retrieve system clock or invalid baudrate setting * \ingroup IFX_SSC_INTERNAL */ static int ifx_ssc_set_baudrate(struct ifx_ssc_port *port, unsigned int baudrate) { /*--- #define DBG_SET_BAUDRATE ---*/ unsigned int ifx_ssc_clock; unsigned int br; int enabled; ifx_ssc_clock = ifx_ssc_get_kernel_clk(port); if (ifx_ssc_clock == 0) { return -EINVAL; } /* Baud rate or kernel clock change needs to calculate the new baud rate */ if ((port->prev_baudrate == baudrate) && (port->prev_ssc_clk == ifx_ssc_clock)) return 0; #ifdef DBG_SET_BAUDRATE printk(KERN_ERR "[%x:%s/%d] baudrate=%u, ifx_ssc_clock=%u CPHY1=%x\n", smp_processor_id(), __FUNCTION__, __LINE__, baudrate, ifx_ssc_clock, IFX_REG_R32(IFX_GPHY1_CFG)); #endif /*--- #ifdef DBG_SET_BAUDRATE ---*/ #ifdef CONFIG_VR9 if (ifx_is_vr9_a21_chip()) { /* VR9 A21 high baudrate support */ u32 reg; if (baudrate > IFX_SSC_SPLIT_BAUD_RATE) { reg = IFX_REG_R32(IFX_GPHY1_CFG); reg &= ~IFX_SSC_HIGH_BAUD_DELAY_MASK; reg |= IFX_SSC_HIGH_BAUD_DELAY_THREE_CLOCK; IFX_REG_W32(reg, IFX_GPHY1_CFG); } else { reg = IFX_REG_R32(IFX_GPHY1_CFG); reg &= ~IFX_SSC_HIGH_BAUD_DELAY_MASK; IFX_REG_W32(reg, IFX_GPHY1_CFG); } } #endif /* CONFIG_VR9 */ /* Compute divider */ br = (((ifx_ssc_clock >> 1) + baudrate / 2) / baudrate) - 1; #ifdef DBG_SET_BAUDRATE printk(KERN_ERR "[%d:%s/%d] br(divider)=%u\n", smp_processor_id(), __FUNCTION__, __LINE__, br); #endif /*--- #ifdef DBG_SET_BAUDRATE ---*/ smp_wmb(); if (br > 0xffff ||((br == 0) && (IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_MASTER))) { printk(KERN_ERR "%s: illegal baudrate %u br %d\n", __func__, baudrate, br); return -EINVAL; } IFX_SSC_IRQ_LOCK(port); /* Have to disable the SSC to set the baudrate */ enabled = (IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_ENABLED) ? 1: 0; IFX_SSC_CONFIG_MODE(port); IFX_SSC_SET_BR(br, port); smp_wmb(); #ifdef CONFIG_AR10 { u32 reg; if (baudrate > IFX_SSC_SPLIT_BAUD_RATE) { reg = IFX_SSC_GET_CON(port); reg &= ~IFX_SSC_CON_CLK_DELAY; reg |= SM(IFX_SSC_CON_CLK_DELAY_DEFAULT, IFX_SSC_CON_CLK_DELAY); IFX_SSC_SET_CON(reg, port); } else { reg = IFX_SSC_GET_CON(port); reg &= ~IFX_SSC_CON_CLK_DELAY; IFX_SSC_SET_CON(reg, port); } } #endif /* CONFIG_AR10 */ if (enabled) { IFX_SSC_RUN_MODE(port); } IFX_SSC_IRQ_UNLOCK(port); port->prev_baudrate = baudrate; port->baudrate = baudrate; port->prev_ssc_clk = ifx_ssc_clock; return 0; } /** * \fn static int ifx_ssc_hwinit (struct ifx_ssc_port *port) * \brief SSC hardware initialization. * Initializes the SSC port hardware with the desired baudrate and transmission * options. * * \param port Pointer to structure #ifx_ssc_port * \return 0 OK * \return -EINVAL Error during initialization * \ingroup IFX_SSC_INTERNAL */ static int ifx_ssc_hwinit(struct ifx_ssc_port *port) { u32 reg; int enabled; /* have to disable the SSC */ enabled = (IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_ENABLED) ? 1: 0; IFX_SSC_CONFIG_MODE(port); if (ifx_ssc_sethwopts(port) < 0) { printk(KERN_ERR "%s: setting the hardware options failed\n", __func__); return -EINVAL; } if (ifx_ssc_set_baudrate(port, port->baudrate) < 0) { printk(KERN_ERR "%s: setting the baud rate failed\n", __func__); return -EINVAL; } IFX_SSC_IRQ_LOCK(port); /* * With this setting it is assured, that if the DMA stalls (RX cannot write * data and TX cannot read data) at a certain point in time, only one * additional TX burst (2 words) + one word from the burst that is just * processing at maximum will be forwarded by the SSC TX FIFO towards * the line side. In this case I assume that at maximum 4 words should be * available within the RX FIFO (2 words from ongoing TX burst + 2 additional * words from the last TX burst requested before the DMA stall (requested as * the TX FIFO level reached 1). In worst case timing may be 6 words remain * within the RX FIFO (I'm not 1000% sure here), but at least we should not * face any overflow. */ /* TX FIFO filling level, half of FIFO size */ reg = SM(1/*(port->tx_fifo_size_words >> 1)*/, IFX_SSC_XFCON_ITL) | IFX_SSC_XFCON_FIFO_FLUSH | IFX_SSC_XFCON_FIFO_ENABLE; IFX_SSC_TX_FIFO_CTRL(reg, port); /* RX FIFO filling level, half of FIFO size */ reg = SM((port->rx_fifo_size_words >> 1), IFX_SSC_XFCON_ITL); reg |= IFX_SSC_XFCON_FIFO_FLUSH | IFX_SSC_XFCON_FIFO_ENABLE; IFX_SSC_RX_FIFO_CTRL(reg, port); if (enabled) { IFX_SSC_RUN_MODE(port); } IFX_SSC_IRQ_UNLOCK(port); return 0; } /** * \fn static int ifx_ssc_start_rx_last_word(struct ifx_ssc_port *port, char *buf, size_t size) * \brief Called to transmit last word <1 ~ 4> bytes for full duplex operation. * * \param port Pointer to structure #ifx_ssc_port * \param buf Pointer to store the received data packet * \param size Amount of bytes to receive * \return none * \ingroup IFX_SSC_INTERNAL */ static void ifx_ssc_start_rx_last_word(struct ifx_ssc_port *port, char *buf, size_t size) { int i; char *p; int rx_cnt = size; ifx_ssc_rxd_t rxd_data; /* Last complete word or incomplete word */ rxd_data.rxd_word = IFX_SSC_GET_RX_WORD(port); for (i = 4 - rx_cnt, p = (char *)buf; i < 4; i++, p++) { *(u8 *)p = rxd_data.rxd_byte.byte[i]; } } /** * \fn static int ifx_ssc_start_tx_last_word(struct ifx_ssc_port *port, char *buf, size_t size) * \brief Called to transmit last word <1 ~ 4> bytes for full duplex operation. * * \param port Pointer to structure #ifx_ssc_port * \param buf Pointer to the data packet to transmit * \param size Amount of bytes to transmit * \return none * \ingroup IFX_SSC_INTERNAL */ #define IFX_SSC_WAIT(_p) IFX_SSC_GET_STATE(_p) & IFX_SSC_STATE_BUSY static void ifx_ssc_start_tx_last_word(struct ifx_ssc_port *port, char *buf, size_t size) { switch (size) { case 1: IFX_SSC_TX_BYTE(*(u8 *)buf, port); break; case 2: IFX_SSC_TX_HALFWORD(*(u16 *)buf, port); break; case 3: IFX_SSC_TX_HALFWORD(*(u16 *)buf, port); while (IFX_SSC_WAIT(port)); /*--- ohne WAIT geht das 3. Byte verloren ---*/ IFX_SSC_TX_BYTE(*(u8 *)(buf + 2), port); break; case 4: IFX_SSC_TX_WORD(*(u32 *)buf, port); break; default: break; } } /** * \fn static void ifx_ssc_start_txfifo(struct ifx_ssc_port *port) * \brief Start FIFO data transmision. * This function copies remaining data in the transmit buffer into the FIFO * * \param port Pointer to structure #ifx_ssc_port * \return number of bytes transmitted * Description: * If txsize is not equal to zero, ssc driver will generate dummy data according * to different cases. * If txsize is equal to zero, just send dummy data whose length is equal to * rxsize for clock generation. * \ingroup IFX_SSC_INTERNAL */ static int ifx_ssc_start_txfifo(struct ifx_ssc_port *port, char *txbuf, size_t txsize) { u32 i; char *p; u32 eff_bytes, eff_word; u32 tx_cnt; u32 *ptxbuf; int total = txsize; #if defined(SSC_TIMING_CHECK) unsigned long ssc_start_time; #endif/*--- #if defined(SSC_TIMING_CHECK) ---*/ IFX_SSC_IRQ_LOCK(port); #if defined(SSC_TIMING_CHECK) ssc_start_time = get_cycles(); #endif/*--- #if defined(SSC_TIMING_CHECK) ---*/ for (;;) { /* Wait for TX FIFO empty */ while (IFX_SSC_TX_FIFO_FILL_LEVEL(port) != 0) { ; } if (total <= 4) { eff_bytes = total; p = (char *)txbuf; ifx_ssc_start_tx_last_word(port, p, eff_bytes); } else if (total > (int)port->tx_fifo_size_bytes) { eff_bytes = port->tx_fifo_size_bytes; eff_word = port->tx_fifo_size_words; for (i = 0, ptxbuf = (u32 *)txbuf; i < eff_word; i++, ptxbuf++) { IFX_SSC_TX_WORD(*ptxbuf, port); } } else { /* 5 ~ 32 bytes */ eff_bytes = total; eff_word = (eff_bytes >> 2) + ((eff_bytes & 0x3) > 0 ? 1 : 0); /* Transmit the complete word */ for (i = 0, ptxbuf = (u32 *)txbuf; i < eff_word - 1; i++, ptxbuf++) { IFX_SSC_TX_WORD(*ptxbuf, port); } tx_cnt = (eff_bytes & 0x3); /* Still one complete word */ if (tx_cnt == 0) { tx_cnt = 4; } ifx_ssc_start_tx_last_word(port, (char *)ptxbuf, tx_cnt); } txbuf += eff_bytes; total -= eff_bytes; if (total == 0){ break; } } #if defined(SSC_TIMING_CHECK) { static int cnt; generic_stat(&ssc_timestat_tx, get_cycles() - ssc_start_time); generic_stat(&ssc_cntstat_tx, txsize); if(cnt++ > 1000) { cnt = 0; display_generic_stat(__func__, &ssc_timestat_tx, 1, 1); display_generic_stat("txsize", &ssc_cntstat_tx, 0, 1); } } #endif/*--- #if defined(SSC_TIMING_CHECK) ---*/ IFX_SSC_IRQ_UNLOCK(port); return txsize; } /** * \fn static void ifx_ssc_start_rxfifo(struct ifx_ssc_port *port) * \brief Start FIFO data reception. * This function processes received data. It will read data from the FIFO * * \param port Pointer to structure #ifx_ssc_port * \return number of bytes received * Description: * In Tx/Rx mode, to void memory copy, where rx data starts must be determined * several special cases * 1) If txsize is divisable by 4, all tx data will be skipped. * 2) If txsize is not divisable by 4,including less than 4 bytes. The remaining 1~3 bytes * have to do swap. * \ingroup IFX_SSC_INTERNAL */ static int ifx_ssc_start_rxfifo(struct ifx_ssc_port *port, char *rxbuf, size_t rxsize) { u32 i; int rx_cnt; u32 eff_word; u32 eff_bytes; u32 *prxbuf; int total = rxsize; #if defined(SSC_TIMING_CHECK) unsigned long ssc_start_time; #endif/*--- #if defined(SSC_TIMING_CHECK) ---*/ IFX_SSC_IRQ_LOCK(port); #if defined(SSC_TIMING_CHECK) ssc_start_time = get_cycles(); #endif/*--- #if defined(SSC_TIMING_CHECK) ---*/ for (;;) { /* In this case, no need to check last complete or incomplete word */ if (total >= (int)port->rx_fifo_size_bytes) { eff_bytes = port->rx_fifo_size_bytes; eff_word = port->rx_fifo_size_words; /* Configure RX request number to start receiving */ IFX_SSC_SET_RXREQ(eff_bytes, port); while(IFX_SSC_RX_FIFO_FILL_LEVEL(port) != eff_word) { ; } for (i = 0, prxbuf = (u32 *)rxbuf; i < eff_word; i++, prxbuf++) { *prxbuf = IFX_SSC_GET_RX_WORD(port); } } else { eff_bytes = total; eff_word = (eff_bytes >> 2) + ((eff_bytes & 0x3) > 0 ? 1 : 0); IFX_SSC_SET_RXREQ(eff_bytes, port); while(IFX_SSC_RX_FIFO_FILL_LEVEL(port) != eff_word) { ; } /* Receive complete word */ for (i = 0, prxbuf = (u32 *)rxbuf; i < eff_word - 1; i++, prxbuf++) { *prxbuf = IFX_SSC_GET_RX_WORD(port); } /* SPI Master know how many bytes to be received */ rx_cnt = total - ((eff_word - 1) << 2); ifx_ssc_start_rx_last_word(port, (char *)prxbuf, rx_cnt); } total -= eff_bytes; rxbuf += eff_bytes; if (total == 0){ break; } } /* Check if any fatal error occurred */ if((IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_RX_OFL)){ printk(KERN_ERR "[%x]%s RX OverFlow, please reduce baudrate(%d)\n", smp_processor_id(), __func__, port->baudrate); ifxssc_reset_rx_fifo(port); ifx_ssc_dump_register(); rxsize = -EIO; } #if defined(SSC_TIMING_CHECK) { static int cnt; generic_stat(&ssc_timestat_rx, get_cycles() - ssc_start_time); generic_stat(&ssc_cntstat_rx, rxsize); if(cnt++ > 1000) { cnt = 0; display_generic_stat(__func__, &ssc_timestat_rx, 1, 1); display_generic_stat("rxsize", &ssc_cntstat_rx, 0, 1); } } #endif/*--- #if defined(SSC_TIMING_CHECK) ---*/ IFX_SSC_IRQ_UNLOCK(port); return rxsize; } /** * \fn static INLINE int ifx_ssc_txfifo(struct ifx_ssc_port *port, char *txbuf, u32 txsize) * \brief Called to transmit data to SSC using FIFO mode. * \param port Pointer to structure #ifx_ssc_port * \param txbuf Pointer to the data packet to transmit * \param txsize Amount of Bytes to transmit * \return >= 0 Number of bytes transmitted * \return < 0 error number * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_txfifo(struct ifx_ssc_port *port, char *txbuf, u32 txsize) { IFX_KASSERT(port->opts.modeRxTx == IFX_SSC_MODE_TX, ("%s invalid txrx mode\n", __func__)); return ifx_ssc_start_txfifo(port, txbuf, txsize); } /** * \fn static INLINE int ifx_ssc_rxfifo(struct ifx_ssc_port *port, char *rxbuf, u32 rxsize) * \brief Called to receive from SSC using FIFO mode. * \param port Pointer to structure #ifx_ssc_port * \param rxbuf Pointer to store the received data packet * \param rxsize Amount of Bytes to receive. * \return >= 0 Number of bytes received * \return < 0 error number * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_rxfifo(struct ifx_ssc_port *port, char *rxbuf, u32 rxsize) { IFX_KASSERT(port->opts.modeRxTx == IFX_SSC_MODE_RX, ("%s invalid txrx mode\n", __func__)); return ifx_ssc_start_rxfifo(port, rxbuf, rxsize); } /** * \fn static INLINE int ifx_ssc_set_spi_mode(ssc_device_t *dev) * \brief SSC set ssc_ mode * Sets the spi mode of the corresponding device. SSC mode is per device * parameter. It is initialized during registeration * * \param dev Pointer to device * \return 0 OK * \return -EBUSY could not set ssc mode because the system is busy * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_set_spi_mode(ssc_device_t *dev) { u32 reg; int val = IFX_SSC_CON_PH; IFX_SSC_CONFIGURE_t *ssc_cfg; struct ifx_ssc_port *port; ssc_cfg = &dev->conn_id; port = dev->port; if (port->prev_ssc_mode == ssc_cfg->ssc_mode) return 0; if ((IFX_SSC_GET_STATE(port) & IFX_SSC_STATE_BUSY) || IFX_SSC_RX_TO_RECEIVED(port) > 0) { printk(KERN_ERR "%s failed to set spi mode\n", __func__); return -EBUSY; } switch(ssc_cfg->ssc_mode) { case IFX_SSC_MODE_0: val = IFX_SSC_CON_PH; break; case IFX_SSC_MODE_1: val = 0; break; case IFX_SSC_MODE_2: val = IFX_SSC_CON_PO | IFX_SSC_CON_PH; break; case IFX_SSC_MODE_3: val = IFX_SSC_CON_PO; break; default: break; } IFX_SSC_IRQ_LOCK(port); IFX_SSC_CONFIG_MODE(port); reg = IFX_SSC_GET_CON(port); reg &= ~(IFX_SSC_CON_PO | IFX_SSC_CON_PH); reg |= (val); smp_wmb(); IFX_SSC_SET_CON(reg, port); smp_wmb(); IFX_SSC_RUN_MODE(port); IFX_SSC_IRQ_UNLOCK(port); port->prev_ssc_mode = ssc_cfg->ssc_mode; return 0; } /** * \fn static INLINE void ifx_ssc_tx_setup( struct ifx_ssc_port *port) * \brief SSC set Tx mode * * \param port Pointer to structure #ifx_ssc_port * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_tx_setup( struct ifx_ssc_port *port) { if (port->opts.modeRxTx != IFX_SSC_MODE_TX) { return ifx_ssc_rxtx_mode_set(port, IFX_SSC_MODE_TX); } return 0; } static int ifx_ssc_tx(struct ssc_device *dev, unsigned char *buf, size_t len) { struct ifx_ssc_port *port = dev->port; int is_write_cmd; int cmd_requires_cs_relock; int ret; if (!buf || !len) return -EINVAL; is_write_cmd = ((buf[0] == IFX_SSC_CMD_PP) || (buf[0] == IFX_SSC_CMD_SE) || (buf[0] == IFX_SSC_CMD_BE) || (buf[0] == IFX_SSC_CMD_BE4K)); // AVM/TKL cmd_requires_cs_relock = ((buf[0] == IFX_SSC_CMD_WREN) || is_write_cmd); if (ifx_ssc_debug_fifo_op) { if (len) _ifx_ssc_debug_ssc_command(__func__, buf[0]); _ifx_ssc_show_buf(__func__, "txbuf", buf, len); } ret = ifx_ssc_tx_setup(port); if (ret < 0) return ret; ret = ifx_ssc_txfifo(port, buf, len); if (ret < 0) return ret; port->stats.txFifo++; /* Count the number of transmitted bytes for this queue entry */ dev->stats.txBytes += len; port->stats.txBytes += len; /* NB, Make sure data has been sent out */ ifx_ssc_wait_finished(port); if (cmd_requires_cs_relock) { ifx_ssc_cs_unlock(dev); udelay(1); ifx_ssc_cs_lock(dev); } if (ifx_ssc_debug_block_after_write_op && is_write_cmd) ifx_ssc_block_until_write_is_complete(dev); return 0; } /** * \fn static INLINE void ifx_ssc_rx_setup( struct ifx_ssc_port *port) * \brief SSC set Rx mode * * \param port Pointer to structure #ifx_ssc_port * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_rx_setup( struct ifx_ssc_port *port) { if (port->opts.modeRxTx != IFX_SSC_MODE_RX) { return ifx_ssc_rxtx_mode_set(port, IFX_SSC_MODE_RX); } return 0; } static int ifx_ssc_rx(struct ssc_device *dev, unsigned char *buf, size_t len) { struct ifx_ssc_port *port = dev->port; int ret; if (!buf || !len) return -EINVAL; ret = ifx_ssc_rx_setup(port); if (ret < 0) return ret; ret = ifx_ssc_rxfifo(port, buf, len); if (ret < 0) return ret; port->stats.rxFifo++; /* Count the number of recevied bytes for this queue entry */ dev->stats.rxBytes += len; port->stats.rxBytes += len; /* NB, Make sure data has been sent out */ ifx_ssc_wait_finished(port); if (ifx_ssc_debug_fifo_op) _ifx_ssc_show_buf(__func__, "rxbuf", buf, len); return 0; } static void ifx_ssc_enqueue_chain(struct chain_queue_head *chain) { struct ssc_device *dev; struct ifx_ssc_port *port; BUG_ON(!chain || !chain->dev || !chain->dev->port); dev = chain->dev; port = dev->port; IFX_SSC_Q_LOCK_BH(port); TAILQ_INSERT_TAIL(&port->ssc_syncq[dev->dev_prio], chain, queue_hook); dev->stats.enqueue++; IFX_SSC_Q_UNLOCK_BH(port); } static inline void ifx_ssc_mark_qelem_not_handled(struct IFX_SSC_QUEUE *qelem) { atomic_set(&qelem->is_handled, 0); } static inline void ifx_ssc_mark_qelem_handled(struct IFX_SSC_QUEUE *qelem) { atomic_set(&qelem->is_handled, 1); } static int ifx_ssc_qelem_is_handled(struct IFX_SSC_QUEUE *qelem) { BUG_ON(!qelem); return atomic_read(&qelem->is_handled); } static void ifx_ssc_enqueue_chain_element(struct chain_queue_head *chain, struct IFX_SSC_QUEUE *qelem) { BUG_ON(!chain || !chain->dev || !chain->dev->port || !qelem); ifx_ssc_mark_qelem_not_handled(qelem); qelem->chain = chain; IFX_SSC_Q_LOCK_BH(chain->dev->port); list_add_tail(&qelem->list, &chain->list); IFX_SSC_Q_UNLOCK_BH(chain->dev->port); } static int ifx_ssc_chain_queue_is_finished(struct chain_queue_head *chain) { struct IFX_SSC_QUEUE *qelem; BUG_ON(!chain); if (!chain->current_qelem) return 0; qelem = list_entry(chain->current_qelem, struct IFX_SSC_QUEUE, list); return ifx_ssc_qelem_is_handled(qelem) && qelem->is_last; } static void ifx_ssc_chain_queue_free(struct chain_queue_head *chain) { struct list_head *cur, *tmp; list_for_each_safe(cur, tmp, &chain->list) { struct IFX_SSC_QUEUE *qelem; qelem = list_entry(cur, struct IFX_SSC_QUEUE, list); if (down_trylock(&qelem->sync_sema)) { up(&qelem->sync_sema); } else { WARN(1, "%s: sync_sema of request 0x%p is still " "locked!", __func__, qelem); continue; } pr_debug("%s: freeing request %p from chain %p\n", __func__, qelem, chain); list_del(cur); kfree(qelem); } WARN_ON(!list_empty(&chain->list)); pr_debug("%s: freeing chain %p\n", __func__, chain); kfree(chain); } static void ifx_ssc_chain_queue_free_if_finished(struct chain_queue_head *chain) { if (ifx_ssc_chain_queue_is_finished(chain)) { if (unlikely(chain->dev)) WARN(1, "%s: chain 0x%p is finished but chain->dev " "still defined! {NOT YET DEQUEUED?}", __func__, chain); else ifx_ssc_chain_queue_free(chain); } } static void ifx_ssc_wait_for_qentry_to_be_served(struct IFX_SSC_QUEUE *queue) { /* When down() disappears, something like this should be used: * * while (down_killable(&queue->sync_sema)) * pr_warn_ratelimited("%s: defering kill signal until " * "qentry is "served\n", __func__); */ down(&queue->sync_sema); } static void ifx_ssc_wake_up_caller(IFX_SSC_QUEUE_t *qentry) { /* Wake up the pending thread */ up(&qentry->sync_sema); } static void ifx_ssc_kthread_wakeup(struct ifx_ssc_port *port) { up(&port->kthread_sema); } static int ifx_ssc_kthread_sleep(struct ifx_ssc_port *port) { int ret; ret = down_interruptible(&port->kthread_sema); if (ret) pr_err("%s: wake up due to signal", __func__); return ret; } static void ifx_ssc_dequeue(struct chain_queue_head *chain) { struct ssc_device *dev; struct ifx_ssc_port *port; WARN_ON(!ifx_ssc_chain_queue_is_finished(chain)); dev = chain->dev; port = dev->port; IFX_SSC_Q_LOCK_BH(port); if (!TAILQ_EMPTY(&port->ssc_syncq[dev->dev_prio])) { TAILQ_REMOVE(&port->ssc_syncq[dev->dev_prio], chain, queue_hook); } dev->stats.dequeue++; chain->dev = NULL; IFX_SSC_Q_UNLOCK_BH(port); } /** * \fn static void ifx_ssc_cs_lock(ssc_device_t *dev) * \brief SSC chip select function, set spi mode, baudrate, call registered * device-specific cs set function. * * \param dev Pointer to structure #ssc_device_t * \return none * \ingroup IFX_SSC_INTERNAL */ static void ifx_ssc_cs_lock(ssc_device_t *dev) { static int old_baud = 0; IFX_SSC_CONFIGURE_t *ssc_cfg; struct ifx_ssc_port *port; port = dev->port; if (port->ssc_cs_locked == IFX_TRUE) { printk(KERN_ERR "Fatal error: %s locked already before\n", __func__); return; } if (old_baud == dev->conn_id.baudrate) { if (dev->conn_id.csset_cb != NULL) { dev->conn_id.csset_cb(IFX_SSC_CS_ON, dev->conn_id.cs_data); } port->ssc_cs_locked = IFX_TRUE; return; } IFX_SSC_PRINT(port, SSC_MSG_LOCK, "%s enter\n", __func__); ssc_cfg = &dev->conn_id; ifx_ssc_set_spi_mode(dev); ifx_ssc_set_baudrate(port, ssc_cfg->baudrate); if (ssc_cfg->csset_cb != NULL) { ssc_cfg->csset_cb(IFX_SSC_CS_ON, ssc_cfg->cs_data); } old_baud = ssc_cfg->baudrate; port->ssc_cs_locked = IFX_TRUE; } /** * \fn static INLINE void ifx_ssc_cs_unlock(ssc_device_t *dev) * \brief SSC chip un select function, call registered device-specific cs reset function. * * \param dev Pointer to structure #ssc_device_t * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE void ifx_ssc_cs_unlock(ssc_device_t *dev) { IFX_SSC_CONFIGURE_t *ssc_cfg; struct ifx_ssc_port *port; port = dev->port; if (port->ssc_cs_locked == IFX_FALSE) { printk(KERN_ERR "Fatal error: %s unlocked already before\n", __func__); return; } ssc_cfg = &dev->conn_id; if(ssc_cfg->csset_cb != NULL) { ssc_cfg->csset_cb(IFX_SSC_CS_OFF, ssc_cfg->cs_data); } port->ssc_cs_locked = IFX_FALSE; IFX_SSC_PRINT(port, SSC_MSG_LOCK, "%s %s exit\n", __func__, dev->dev_name); } /** * \fn int ifx_sscSetBaud(IFX_SSC_HANDLE handler, unsigned int baud) * \brief Configures the Baudrate of a given connection. * * The baudrate can also be change multiple times * for a single connection. The baudrate change * will take place for the next call of ifx_SscTx, * ifx_SscRx or ifx_SscTxRx. * * \param handler Handle of the connection where to make the * configuration on * \param baud Baudrate to configure. This value can be rounded * during the calculation of the SSC clock divider * * \return = 0 OK * \return < 0 error number * \ingroup IFX_SSC_FUNCTIONS */ int ifx_sscSetBaud(IFX_SSC_HANDLE handler, unsigned int baud) { ssc_device_t *dev; IFX_SSC_CONFIGURE_t *ssc_cfg; /* Sanity check */ IFX_KASSERT((handler != NULL), ("%s Invalid parameter\n", __func__)); dev = (ssc_device_t *)handler; ssc_cfg = &dev->conn_id; /* XXX, protection because of being used in other places */ ssc_cfg->baudrate = baud; return 0; } EXPORT_SYMBOL(ifx_sscSetBaud); static int ifx_ssc_alloc_and_enqueue_chain_queue( struct ssc_device *dev, struct chain_queue_head **chain, enum chain_queue_flags flag) { if (*chain && (flag != CHAIN_QUEUE_START)) return 0; *chain = kmalloc(sizeof **chain, GFP_KERNEL); pr_debug("%s: kmalloc(chain=%u) = %p\n", __func__, sizeof **chain, *chain); WARN_ON(!*chain); if (!*chain) return -ENOMEM; memset(*chain, 0, sizeof(**chain)); (*chain)->dev = dev; INIT_LIST_HEAD(&(*chain)->list); ifx_ssc_enqueue_chain(*chain); return 0; } static int ifx_ssc_alloc_and_init_queue_element(struct IFX_SSC_QUEUE **_qelem, struct IFX_SSC_QUEUE *template, enum chain_queue_flags flag) { struct IFX_SSC_QUEUE *qelem; qelem = *_qelem = kmalloc(sizeof(*qelem), GFP_KERNEL); pr_debug("%s: kmalloc(qelem=%u) = %p\n", __func__, sizeof *qelem, qelem); BUG_ON(!qelem); if (!qelem) return -ENOMEM; *qelem = *template; qelem->is_last = !!(flag == CHAIN_QUEUE_FINISH); return 0; } /** *\fn int ifx_sscTxRx (IFX_SSC_HANDLE handler, char *txbuf, u32 txsize, char *rxbuf, u32 rxsize) *\brief Called to transmit/receive to/from SSC in one step. * This means that the data transmission and reception is done in parallel. * No DMA is possible here. The SSC driver sets the chipselect when the * data transmission starts and resets it when the transmission is * completed. The transmit and receive buffer memory allocation and * de-allocation is done by the SSC client. * * \param handler Handle of the connection where to make the * configuration on * \param txbuf Pointer to the data packet to transmit * \param txsize Amount of Bytes to transmit * \param rxbuf Pointer to store the received data packet * \param rxsize Amount of Bytes to receive. * \return >= 0 Number of bytes received (if rxbuf != 0) or transmitted * \return < 0 error number * \ingroup IFX_SSC_FUNCTIONS */ int ifx_sscTxRx(IFX_SSC_HANDLE handler, char *txbuf, u32 txsize, char *rxbuf, u32 rxsize, struct chain_queue_head **chain_queue_head, enum chain_queue_flags flag) { struct chain_queue_head *_dummy = NULL; ssc_device_t *dev; IFX_SSC_CONFIGURE_t *ssc_cfg; IFX_SSC_QUEUE_t *pqueue; struct ifx_ssc_port *port; struct ifx_ssc_device_stats *stats; int ret; /* Sanity check */ IFX_KASSERT((handler != NULL), ("%s Invalid parameter\n", __func__)); dev = (ssc_device_t *)handler; stats = &dev->stats; port = dev->port; if (in_interrupt()) { stats->context_err++; if(stats->context_err < 5) { printk(KERN_ERR "%s can't be called in interupt context< irq, softirq, tasklet>%s\n", __func__, stats->context_err == 4 ? "... supressing further error reports\n" : "" ); dump_stack(); } return 0; } ssc_cfg = &dev->conn_id; if ((int)txsize > ssc_cfg->fragSize || (int)rxsize > ssc_cfg->fragSize) { stats->frag_err++; printk(KERN_ERR "%s Device driver must do its own fragmentation tx %d rx %d > %d\n", __func__, txsize, rxsize, ssc_cfg->fragSize); return 0; } /* * Ensure that only asynchronous SSC Handles could enqueue a * synchronous request. The parameter 'handle_type' is set during the * ConnId allocation process. */ if (dev->queue.handle_type != IFX_SSC_HANDL_TYPE_SYNC) { stats->handler_err++; printk(KERN_ERR "%s must use sync handler\n", __func__); return 0; } BUG_ON(!chain_queue_head && flag != CHAIN_QUEUE_FINISH); chain_queue_head = chain_queue_head ? : &_dummy; ret = ifx_ssc_alloc_and_enqueue_chain_queue(dev, chain_queue_head, flag); if (ret < 0) return ret; ret = ifx_ssc_alloc_and_init_queue_element(&pqueue, &dev->queue, flag); if (ret < 0) return ret; /* Add pointer and sizes to the queue entry of this SSC handle. */ pqueue->txbuf = txbuf; pqueue->txsize = txsize; pqueue->rxbuf = rxbuf; pqueue->rxsize = rxsize; sema_init(&pqueue->sync_sema, 0); /* Add queue entry to priority queue */ ifx_ssc_enqueue_chain_element(*chain_queue_head, pqueue); if (unlikely(ifx_ssc_qelem_is_handled(pqueue))) { pr_warn("%s: enqueued chain element 0x%p is handled before kthread " "wake-up\n", __func__, *chain_queue_head); ifx_ssc_debug_port_chains(port); } IFX_SSC_PRINT(port, SSC_MSG_THREAD, "%s wake up ssc kernel thread\n", __func__); ifx_ssc_kthread_wakeup(port); ifx_ssc_wait_for_qentry_to_be_served(pqueue); ret = pqueue->txsize + pqueue->rxsize; ifx_ssc_chain_queue_free_if_finished(pqueue->chain); return ret; } EXPORT_SYMBOL(ifx_sscTxRx); void ifx_ssc_chain_finish(IFX_SSC_HANDLE handler, struct chain_queue_head **chain_queue) { ifx_sscTxRx(handler, NULL, 0, NULL, 0, chain_queue, CHAIN_QUEUE_FINISH); } EXPORT_SYMBOL(ifx_ssc_chain_finish); /** *\fn int ifx_sscTx(IFX_SSC_HANDLE handler, char *txbuf, u32 txsize) *\brief Called to transmit the data. * transmission starts and resets it when the transmission * the transmit buffer is done by the SSC client. * * \param handler Handle of the connection where to make the * configuration on * \param txbuf Pointer to the data packet to transmit * \param txsize Amount of Bytes to transmit * \return >= 0 Number of bytes transmitted * \return < 0 error number * \ingroup IFX_SSC_FUNCTIONS */ int ifx_sscTx(IFX_SSC_HANDLE handler, char *txbuf, u32 txsize, struct chain_queue_head **chain_queue, enum chain_queue_flags flag) { return ifx_sscTxRx(handler, txbuf, txsize, NULL, 0, chain_queue, flag); } EXPORT_SYMBOL(ifx_sscTx); /** *\fn int ifx_sscRx(IFX_SSC_HANDLE handler, char *rxbuf, u32 rxsize) *\brief Called to receive the data. * The SSC driver sets the chipselect when the data reception starts and * resets it when the reception is completed. The memory allocation and * de-allocation of the receive buffer is done by the SSC client. * * \param handler Handle of the connection where to make the * configuration on * \param rxbuf Pointer to the data packet to be received * \param rxsize Amount of Bytes to be received * \return >= 0 Number of bytes received * \return < 0 error number * \ingroup IFX_SSC_FUNCTIONS */ int ifx_sscRx(IFX_SSC_HANDLE handler, char *rxbuf, u32 rxsize, struct chain_queue_head **chain_queue, enum chain_queue_flags flag) { return ifx_sscTxRx(handler, NULL, 0, rxbuf, rxsize, chain_queue, flag); } EXPORT_SYMBOL (ifx_sscRx); static struct chain_queue_head *ifx_ssc_current_chain(struct ifx_ssc_port *port) { struct chain_queue_head *chain = NULL; int i; IFX_SSC_Q_LOCK_BH(port); /* Choose the highest queue entry first */ for (i = IFX_SSC_PRIO_HIGH; i >= IFX_SSC_PRIO_LOW; i--) { if (!TAILQ_EMPTY(&port->ssc_syncq[i])){ chain = TAILQ_FIRST(&port->ssc_syncq[i]); break; } } IFX_SSC_Q_UNLOCK_BH(port); return chain; } static struct IFX_SSC_QUEUE *ifx_ssc_current_qelem( struct chain_queue_head *chain) { struct IFX_SSC_QUEUE *qelem; if (!chain || list_empty(&chain->list)) return NULL; if (!chain->current_qelem) { chain->current_qelem = chain->list.next; } BUG_ON(!chain->current_qelem); qelem = list_entry(chain->current_qelem, struct IFX_SSC_QUEUE, list); if (ifx_ssc_qelem_is_handled(qelem)) { if (list_is_last(&qelem->list, &chain->list)) return qelem; chain->current_qelem = qelem->list.next; } return list_entry(chain->current_qelem, struct IFX_SSC_QUEUE, list); } static void _ifx_ssc_debug_ssc_command(const char *prefix, unsigned char cmd) { struct ssc_cmd { const char *cmd; unsigned char index; } ssc_cmds[] = { { .index = IFX_SSC_CMD_READ, .cmd = "READ", }, { .index = IFX_SSC_CMD_FAST_READ, .cmd = "FAST_READ", }, { .index = IFX_SSC_CMD_RDID, .cmd = "RDID", }, { .index = IFX_SSC_CMD_WREN, .cmd = "WREN", }, { .index = IFX_SSC_CMD_WRDI, .cmd = "WRDI", }, { .index = IFX_SSC_CMD_SE, .cmd = "SE", }, { .index = IFX_SSC_CMD_BE, .cmd = "BE", }, { .index = IFX_SSC_CMD_BE4K, .cmd = "BE4K", }, // AVM/TKL { .index = IFX_SSC_CMD_PP, .cmd = "PP", }, { .index = IFX_SSC_CMD_RDSR, .cmd = "RDSR", }, { .index = IFX_SSC_CMD_WRSR, .cmd = "WRSR", }, { .index = IFX_SSC_CMD_DP, .cmd = "DP", }, { .index = IFX_SSC_CMD_RES, .cmd = "RES", }, { .cmd = NULL, }, }; struct ssc_cmd *c = ssc_cmds; while (c->cmd && c->index != cmd) c++; pr_err("%s: COMMAND: %02x %s\n", prefix, cmd, c->cmd ? : "(unknown command)"); } static void _ifx_ssc_show_buf(const char *prefix, const char *name, char *buf, size_t len) { size_t off = 0; while (off < len) { size_t this_len = (len - off) > 64 ? 64 : (len - off); pr_err("%s%s%s@0x%p (bytes %zd-%zd) = {%s %*ph %s}\n", prefix ? : "", prefix ? ": " : "", name, buf + off, off, off + this_len, off ? " ..." : "", this_len, &buf[off], (off + this_len < len) ? "... " : ""); off += this_len; } } static void ifx_ssc_debug_chain(struct chain_queue_head *chain, const char *_prefix) { struct list_head *pos; char *prefix = (char *)_prefix; unsigned i; if (!prefix) prefix = kasprintf(GFP_KERNEL, "%s%s", prefix ? : __func__, prefix ? "" : ":"); pr_debug("%s(struct ssc_device *)dev = %p\n" "%s(struct list_head *)current_qelem = %p\n" "%s(struct list_head *)list = {\n", prefix, chain->dev, prefix, chain->current_qelem, prefix); i = 0; list_for_each(pos, &chain->list) { struct IFX_SSC_QUEUE *qelem; qelem = list_entry(pos, struct IFX_SSC_QUEUE, list); pr_debug("%s qelem[%u]@%p%s%s\n", prefix, i++, qelem, atomic_read(&qelem->is_handled) ? " (handled)" : "", qelem->is_last ? " (last-in-chain)" : ""); } pr_debug("%s}\n", prefix); if (prefix != _prefix) kfree(prefix); } static void ifx_ssc_debug_port_chains(struct ifx_ssc_port *port) { struct chain_queue_head *chain; char *sub_prefix; int i; sub_prefix = kasprintf(GFP_KERNEL, "%s: ", __func__); pr_debug("%s: port@%p: {\n", __func__, port); IFX_SSC_Q_LOCK_BH(port); for (i = IFX_SSC_PRIO_HIGH; i >= IFX_SSC_PRIO_LOW; i--) { pr_debug("%s: priority = %u: {\n", __func__, i); TAILQ_FOREACH(chain, &port->ssc_syncq[i], queue_hook) { pr_debug("%s: chain@%p {\n", __func__, chain); ifx_ssc_debug_chain(chain, sub_prefix); pr_debug("%s: }\n", __func__); } pr_debug("%s: }\n", __func__); } IFX_SSC_Q_UNLOCK_BH(port); pr_debug("%s: }\n", __func__); kfree(sub_prefix); } static int ifx_ssc_block_until_write_is_complete(struct ssc_device *dev) { unsigned char cmd = IFX_SSC_CMD_RDSR; unsigned char buf[] = { 0x5a, }; int ret; do { ifx_ssc_cs_unlock(dev); schedule(); ifx_ssc_cs_lock(dev); ret = ifx_ssc_tx(dev, &cmd, 1); if (ret < 0) { pr_err("%s: unable to send RDSR command (error: %d)\n", __func__, ret); return ret; } ret = ifx_ssc_rx(dev, buf, sizeof(buf)); if (ret < 0) { pr_err("%s: unable to receive reply for RDSR command " "(error: %d)\n", __func__, ret); break; } if (buf[0] == 0xff) { pr_err("%s: Unable to get status register!\n", __func__); break; } if (!(buf[0] & IFX_SSC_STATUS_REG_WIP)) break; pr_debug("%s: Write-In-Progress bit is still set; " "blocking...\n", __func__); } while (1); return ret; } static int ifx_ssc_serve_qentry(struct ifx_ssc_port *port, unsigned *pending_requests) { struct chain_queue_head *chain; IFX_SSC_QUEUE_t *qentry = NULL; ssc_device_t *dev; IFX_SSC_CONFIGURE_t *ssc_cfg; chain = ifx_ssc_current_chain(port); if (!chain) { pr_err_ratelimited("%s: kthread awoke w/o pending request " "chain\n", __func__); if (*pending_requests) pr_warn_ratelimited("%s: resetting pending_requests " "from %u to 0\n", __func__, *pending_requests); *pending_requests = 0; ifx_ssc_debug_port_chains(port); return -EINVAL; } if (ifx_ssc_chain_queue_is_finished(chain)) { pr_err_ratelimited("%s: kthread awoke but finished chain 0x%p " "is at top of queue", __func__, chain); if (*pending_requests) pr_warn_ratelimited("%s: resetting pending_requests " "from %u to 0\n", __func__, *pending_requests); *pending_requests = 0; ifx_ssc_debug_port_chains(port); pr_err("%s: dequeueing finished chain 0x%p\n", __func__, chain); dev = chain->dev; ifx_ssc_dequeue(chain); if (dev) ifx_ssc_cs_unlock(dev); return -EINVAL; } qentry = ifx_ssc_current_qelem(chain); if (!qentry || ifx_ssc_qelem_is_handled(qentry)) { pr_err("%s: unable to get unhandled queue entry: qentry@%p: " "is %shandled%s\n", __func__, qentry, qentry ? ifx_ssc_qelem_is_handled(qentry) ? "" : "not " : "definitly not", qentry ? qentry->is_last ? " (is_last)" : "" : ""); (*pending_requests)++; pr_info("%s: increasing pending_requests to %u\n", __func__, *pending_requests); ifx_ssc_debug_port_chains(port); show_state_filter(TASK_UNINTERRUPTIBLE); return -ENOENT; } /* Get connection handle */ dev = qentry->dev; ssc_cfg = &dev->conn_id; if ((chain->list.next == chain->current_qelem) && !dev->port->ssc_cs_locked) ifx_ssc_cs_lock(dev); if (qentry->txbuf != NULL) ifx_ssc_tx(dev, qentry->txbuf, qentry->txsize); if (qentry->rxbuf != NULL) ifx_ssc_rx(dev, qentry->rxbuf, qentry->rxsize); ifx_ssc_mark_qelem_handled(qentry); if (ifx_ssc_chain_queue_is_finished(chain)) { ifx_ssc_dequeue(chain); ifx_ssc_cs_unlock(dev); } ifx_ssc_wake_up_caller(qentry); return 0; } #define IFX_SSC_THREAD_OPTIONS (CLONE_FS | CLONE_FILES | CLONE_SIGHAND) /** * \fn static int ifx_ssc_kthread(void *arg) * \brief SSC kernel thread implementation function * * \param arg cast to structure #ifx_ssc_port * \return none * \ingroup IFX_SSC_INTERNAL */ static int ifx_ssc_kthread(void *arg) { struct ifx_ssc_port *port = (struct ifx_ssc_port *)arg; unsigned pending_requests = 0; while (!kthread_should_stop()) { int signaled; /* Serve queue entries till no queue entry anymore to serve, we * wait for DMA or the lock entry is not in the queue. */ signaled = ifx_ssc_kthread_sleep(port); if (signaled) continue; do { int failure; failure = ifx_ssc_serve_qentry(port, &pending_requests); if (failure || !pending_requests) break; schedule(); pr_info("%s: %u pending request%s, retrying to serve\n", __func__, pending_requests, pending_requests == 1 ? "" : "s"); } while (pending_requests--); } return 0; } /** * \fn static INLINE int ifx_ssc_thread_init(struct ifx_ssc_port *port) * \brief SSC kernel thread initialization * * \param port Pointer to structure #ifx_ssc_port * \return none * \ingroup IFX_SSC_INTERNAL */ static INLINE int ifx_ssc_thread_init(struct ifx_ssc_port *port) { sema_init(&port->kthread_sema, 0); port->ssc_kthread = kthread_run(ifx_ssc_kthread, port, "ifx_sscd"); IFX_SSC_PRINT(port, SSC_MSG_INIT, "%s: started kthread ifx_sscd\n", __func__); return 0; } #ifdef CONFIG_SYSCTL /* * Deal with the sysctl handler api changing. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) #define SSC_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ f(ctl_table *ctl, int write, struct file *filp, \ void __user *buffer, size_t *lenp) #define SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ proc_dointvec(ctl, write, filp, buffer, lenp) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) #define SSC_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ f(ctl_table *ctl, int write, struct file *filp, \ void __user *buffer, size_t *lenp, loff_t *ppos) #define SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ proc_dointvec(ctl, write, filp, buffer, lenp, ppos) #else /* Linux 2.6.32+ */ #define SSC_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ f(ctl_table *ctl, int write, \ void __user *buffer, size_t *lenp, loff_t *ppos) #define SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ proc_dointvec(ctl, write, buffer, lenp, ppos) #endif /* SSC Client driver proc entry for parameter configuration */ enum { IFX_SSC_PRIV_FRAGMENT_SIZE = 1, IFX_SSC_PRIV_FIFO_SIZE = 2, IFX_SSC_PRIV_BAUDRATE = 3, IFX_SSC_PRIV_MODE = 4, }; static int SSC_SYSCTL_DECL(ssc_sysctl_private, ctl, write, filp, buffer, lenp, ppos) { ssc_device_t *dev = ctl->extra1; IFX_SSC_CONFIGURE_t *ssc_cfg = &dev->conn_id; struct ifx_ssc_port *port; u32 val; int ret; port = dev->port; ctl->data = &val; ctl->maxlen = sizeof(val); if (write) { ret = SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,lenp, ppos); if (ret == 0) { switch ((long)ctl->extra2) { case IFX_SSC_PRIV_FRAGMENT_SIZE: if (val < IFX_SSC_MIN_FRAGSIZE || val > IFX_SSC_MAX_FRAGSIZE) return -EINVAL; ssc_cfg->fragSize = val; port->ssc_fragSize = val; break; case IFX_SSC_PRIV_FIFO_SIZE: if (val < IFX_SSC_FIFO_MIN_THRESHOULD || val > IFX_SSC_FIFO_MAX_THRESHOULD) return -EINVAL; ssc_cfg->maxFIFOSize = val; break; case IFX_SSC_PRIV_BAUDRATE: /* XXX, sanity check */ ssc_cfg->baudrate = val; break; case IFX_SSC_PRIV_MODE: ret = -EINVAL; break; default: return -EINVAL; } } } else { switch ((long)ctl->extra2) { case IFX_SSC_PRIV_FRAGMENT_SIZE: val = ssc_cfg->fragSize; break; case IFX_SSC_PRIV_FIFO_SIZE: val = ssc_cfg->maxFIFOSize; break; case IFX_SSC_PRIV_BAUDRATE: val = ssc_cfg->baudrate; break; case IFX_SSC_PRIV_MODE: val = ssc_cfg->ssc_mode; break; default: return -EINVAL; } ret = SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos); } return ret; } static const ctl_table ssc_sysctl_template[] = { /* NB: must be last entry before NULL */ { IFX_INIT_CTL_NAME(CTL_AUTO) .procname = "fragment_size", .mode = 0644, .proc_handler = ssc_sysctl_private, .extra2 = (void *)IFX_SSC_PRIV_FRAGMENT_SIZE, }, { IFX_INIT_CTL_NAME(CTL_AUTO) .procname = "fifosize", .mode = 0644, .proc_handler = ssc_sysctl_private, .extra2 = (void *)IFX_SSC_PRIV_FIFO_SIZE, }, { IFX_INIT_CTL_NAME(CTL_AUTO) .procname = "baudrate", .mode = 0644, .proc_handler = ssc_sysctl_private, .extra2 = (void *)IFX_SSC_PRIV_BAUDRATE, }, { IFX_INIT_CTL_NAME(CTL_AUTO) .procname = "spimode", .mode = 0644, .proc_handler = ssc_sysctl_private, .extra2 = (void *)IFX_SSC_PRIV_MODE, }, { 0 } }; static void ifx_ssc_sysctl_attach(ssc_device_t *dev) { int i, space; space = 5 * sizeof(struct ctl_table) + sizeof(ssc_sysctl_template); dev->ssc_sysctls = kmalloc(space, GFP_KERNEL); if (dev->ssc_sysctls == NULL) { printk("%s: no memory for sysctl table!\n", __func__); return; } /* setup the table */ memset(dev->ssc_sysctls, 0, space); IFX_SET_CTL_NAME(dev->ssc_sysctls[0], CTL_DEV); dev->ssc_sysctls[0].procname = "dev"; dev->ssc_sysctls[0].mode = 0555; dev->ssc_sysctls[0].child = &dev->ssc_sysctls[2]; /* [1] is NULL terminator */ IFX_SET_CTL_NAME(dev->ssc_sysctls[2], CTL_AUTO); dev->ssc_sysctls[2].procname = dev->dev_name; dev->ssc_sysctls[2].mode = 0555; dev->ssc_sysctls[2].child = &dev->ssc_sysctls[4]; /* [3] is NULL terminator */ /* copy in pre-defined data */ memcpy(&dev->ssc_sysctls[4], ssc_sysctl_template, sizeof(ssc_sysctl_template)); /* add in dynamic data references */ for (i = 4; dev->ssc_sysctls[i].procname; i++){ if (dev->ssc_sysctls[i].extra1 == NULL) { dev->ssc_sysctls[i].extra1 = dev; } } /* tack on back-pointer to parent device */ dev->ssc_sysctls[i - 1].data = dev->dev_name; /* and register everything */ dev->ssc_sysctl_header = IFX_REGISTER_SYSCTL_TABLE(dev->ssc_sysctls); if (dev->ssc_sysctl_header == NULL ) { printk("%s: failed to register sysctls!\n", dev->dev_name); kfree(dev->ssc_sysctls); dev->ssc_sysctls = NULL; } } static void ifx_ssc_sysctl_detach(ssc_device_t *dev) { if (dev->ssc_sysctl_header != NULL) { unregister_sysctl_table(dev->ssc_sysctl_header); dev->ssc_sysctl_header = NULL; } if (dev->ssc_sysctls != NULL) { kfree(dev->ssc_sysctls); dev->ssc_sysctls = NULL; } } /* SSC Driver itself proc support for debug and future configuration */ enum { IFX_SSC_PRIV_DEBUG = 1, }; static int SSC_SYSCTL_DECL(port_sysctl_private, ctl, write, filp, buffer, lenp, ppos) { struct ifx_ssc_port *port = ctl->extra1; u32 val; int ret; ctl->data = &val; ctl->maxlen = sizeof(val); if (write) { ret = SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,lenp, ppos); if (ret == 0) { switch ((long)ctl->extra2) { case IFX_SSC_PRIV_DEBUG: port->ssc_debug = val; break; default: return -EINVAL; } } } else { switch ((long)ctl->extra2) { case IFX_SSC_PRIV_DEBUG: val = port->ssc_debug; break; default: return -EINVAL; } ret = SSC_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos); } return ret; } static const ctl_table port_sysctl_template[] = { /* NB: must be last entry before NULL */ { IFX_INIT_CTL_NAME(CTL_AUTO) .procname = "debug", .mode = 0644, .proc_handler = port_sysctl_private, .extra2 = (void *)IFX_SSC_PRIV_DEBUG, }, { 0 } }; static void ifx_ssc_port_sysctl_attach(struct ifx_ssc_port *port) { int i, space; space = 5 * sizeof(struct ctl_table) + sizeof(port_sysctl_template); port->port_sysctls = kmalloc(space, GFP_KERNEL); if (port->port_sysctls == NULL) { printk("%s: no memory for sysctl table!\n", __func__); return; } /* setup the table */ memset(port->port_sysctls, 0, space); IFX_SET_CTL_NAME(port->port_sysctls[0], CTL_DEV); port->port_sysctls[0].procname = "dev"; port->port_sysctls[0].mode = 0555; port->port_sysctls[0].child = &port->port_sysctls[2]; /* [1] is NULL terminator */ IFX_SET_CTL_NAME(port->port_sysctls[2], CTL_AUTO); port->port_sysctls[2].procname = port->name; port->port_sysctls[2].mode = 0555; port->port_sysctls[2].child = &port->port_sysctls[4]; /* [3] is NULL terminator */ /* copy in pre-defined data */ memcpy(&port->port_sysctls[4], port_sysctl_template, sizeof(port_sysctl_template)); /* add in dynamic data references */ for (i = 4; port->port_sysctls[i].procname; i++){ if (port->port_sysctls[i].extra1 == NULL) { port->port_sysctls[i].extra1 = port; } } /* tack on back-pointer to parent device */ port->port_sysctls[i - 1].data = port->name; /* and register everything */ port->port_sysctl_header = IFX_REGISTER_SYSCTL_TABLE(port->port_sysctls); if (port->port_sysctl_header == NULL ) { printk("%s: failed to register sysctls!\n", port->name); kfree(port->port_sysctls); port->port_sysctls = NULL; } } static void ifx_ssc_port_sysctl_detach(struct ifx_ssc_port *port) { if (port->port_sysctl_header != NULL) { unregister_sysctl_table(port->port_sysctl_header); port->port_sysctl_header = NULL; } if (port->port_sysctls != NULL) { kfree(port->port_sysctls); port->port_sysctls = NULL; } } #endif /* CONFIG_SYSCTL */ /** *\fn IFX_SSC_HANDLE ifx_sscAllocConnection (char *dev_name, IFX_SSC_CONFIGURE_t *connid) *\brief Allocate and create a Connection ID "ConnId" * * Allocate and create a Connection ID "ConnId" to communicate over SSC. * This ConnId is needed for all remaining SSC driver API calls. This * ConnId is a handle that helps the SSC driver to find the configuration * that belongs to the connection. ConnId specific parameters are e.g. * Baudrate, Priority, Chipselect Callback, etc. * * \param dev_name unique name for this connection. If null, will alloc * one unique name automatically * \param connid Connectin id * \return a handle "IFX_SSC_HANDLE" in case the allocation was successful. * In case of an error, the return handle is zero (NULL). * \ingroup IFX_SSC_FUNCTIONS */ IFX_SSC_HANDLE ifx_sscAllocConnection(char *dev_name, IFX_SSC_CONFIGURE_t *connid) { struct ifx_ssc_port *port; ssc_device_t *p; ssc_device_t *q; IFX_SSC_QUEUE_t *queue; char buf[IFX_SSC_MAX_DEVNAME] = {0}; char *pName; /* Sanity check first! */ if (ifx_ssc_isp == NULL) { printk("%s ssc driver must be loaded first!\n", __func__); return NULL; } port = &ifx_ssc_isp[0]; /* XXX */ if (port->ssc_ndevs >= IFX_SSC_MAX_DEVICE) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s device number out of range\n", __func__); return NULL; } if (connid == NULL) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s must provide connection portrmation!\n", __func__); return NULL; } if ((connid->ssc_mode < IFX_SSC_MODE_0) || (connid->ssc_mode > IFX_SSC_MODE_3)) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s invalid spi mode <%d~%d>!\n", __func__, IFX_SSC_MODE_0, IFX_SSC_MODE_3); return NULL; } if (connid->ssc_prio < IFX_SSC_PRIO_LOW || (connid->ssc_prio > IFX_SSC_PRIO_MAX)) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s invalid priority <%d~%d>!\n", __func__, IFX_SSC_PRIO_LOW, IFX_SSC_PRIO_MAX); } if (connid->csset_cb == NULL) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s must provide cs function\n", __func__); return NULL; } if (connid->fragSize < IFX_SSC_MIN_FRAGSIZE || connid->fragSize > IFX_SSC_MAX_FRAGSIZE) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s %d invalid fragment size <%d~%d>!\n", __func__, connid->fragSize,IFX_SSC_MIN_FRAGSIZE, IFX_SSC_MAX_FRAGSIZE); return NULL; } if (connid->maxFIFOSize < IFX_SSC_FIFO_MIN_THRESHOULD || connid->maxFIFOSize > IFX_SSC_FIFO_MAX_THRESHOULD) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s %d invalid fifo size <%d~%d>!\n", __func__, connid->maxFIFOSize, IFX_SSC_FIFO_MIN_THRESHOULD, IFX_SSC_FIFO_MAX_THRESHOULD); return NULL; } if (connid->duplex_mode != IFX_SSC_FULL_DUPLEX && connid->duplex_mode != IFX_SSC_HALF_DUPLEX) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s %d invalid duplex mode <%d~%d>!\n", __func__, connid->duplex_mode, IFX_SSC_FULL_DUPLEX, IFX_SSC_HALF_DUPLEX); return NULL; } /* If no name specified, will assign one name for identification */ if (dev_name == NULL) { sprintf(buf, "ssc%d", port->ssc_ndevs); pName = buf; } else { if (strlen(dev_name) > (IFX_SSC_MAX_DEVNAME - 1) ) { IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s device name is too long\n", __func__); return NULL; } pName = dev_name; } p = (ssc_device_t *)kmalloc(sizeof (ssc_device_t), GFP_KERNEL); if (p == NULL) { IFX_SSC_PRINT(port, SSC_MSG_ERROR,"%s failed to allocate memory\n", __func__); return NULL; } memset(p, 0, sizeof (ssc_device_t)); IFX_SSC_SEM_LOCK(port->dev_sem); TAILQ_FOREACH(q, &port->ssc_devq, dev_entry) { if (strcmp(q->dev_name, pName) == 0) { kfree(p); IFX_SSC_SEM_UNLOCK(port->dev_sem); IFX_SSC_PRINT(port, SSC_MSG_ERROR, "%s device registered already!\n", __func__); return NULL; } } IFX_SSC_SEM_UNLOCK(port->dev_sem); /* Follow net device driver name rule */ memcpy(p->dev_name, pName, IFX_SSC_MAX_DEVNAME); p->duplex = connid->duplex_mode; memcpy((char *)&p->conn_id, (char *)connid, sizeof (IFX_SSC_CONFIGURE_t)); queue = &p->queue; queue->handle_type = IFX_SSC_HANDL_TYPE_SYNC; /* Back pointer to later usage */ queue->dev = p; /* * Just for fast access, priority based on device, instead of packet * Still keep per packet priority there for future change. */ p->dev_prio = connid->ssc_prio; IFX_SSC_WAKELIST_INIT(p->dev_thread_wait); p->port = port; /* back pointer to port for easy reference later */ port->ssc_ndevs++; #ifdef CONFIG_SYSCTL ifx_ssc_sysctl_attach(p); #endif /* CONFIG_SYSCTL */ IFX_SSC_SEM_LOCK(port->dev_sem); TAILQ_INSERT_TAIL(&port->ssc_devq, p, dev_entry); IFX_SSC_SEM_UNLOCK(port->dev_sem); /* Make sure very device CS in default state on registration */ if(connid->csset_cb != NULL) { connid->csset_cb(IFX_SSC_CS_OFF, connid->cs_data); } IFX_SSC_PRINT(port, SSC_MSG_INIT, "%s: device %s register sucessfully!\n", __func__, p->dev_name); return (IFX_SSC_HANDLE)p; } EXPORT_SYMBOL(ifx_sscAllocConnection); /** *\fn int ifx_sscFreeConnection (IFX_SSC_HANDLE handler) *\brief Release ssc connnection * * Release a ConnId handle that was allocated by the function ifx_SscAllocConnection * before. An allocated ConnId has to be released by the client driver module * when the SSC driver is not used anymore. Note that all allocated ConnId's should * be released before the SSC driver is unloaded from the kernel. * * \param handler ConnId handle allocated by ifx_SscAllocConnection * \returns (0) in case of success, otherwise (-1) in case of errors. * \ingroup IFX_SSC_FUNCTIONS */ int ifx_sscFreeConnection(IFX_SSC_HANDLE handler) { ssc_device_t *p; struct ifx_ssc_port *port; ssc_device_t *q, *next; IFX_KASSERT((handler != NULL), ("%s Invalid parameter\n", __func__)); p = (ssc_device_t *)handler; port = p->port; IFX_SSC_SEM_LOCK(port->dev_sem); TAILQ_FOREACH_SAFE(q, &port->ssc_devq, dev_entry, next) { if (strcmp(q->dev_name, p->dev_name) == 0) { TAILQ_REMOVE(&port->ssc_devq, q, dev_entry); #ifdef CONFIG_SYSCTL ifx_ssc_sysctl_detach(q); #endif /* CONFIG_SYSCTL */ kfree(q); port->ssc_ndevs--; IFX_SSC_SEM_UNLOCK(port->dev_sem); IFX_SSC_PRINT(port, SSC_MSG_INIT, "%s device %s unregistered\n", __func__, p->dev_name); return 0; } } IFX_SSC_SEM_UNLOCK(port->dev_sem); return -1; } EXPORT_SYMBOL(ifx_sscFreeConnection); /** * \fn static inline int ifx_ssc_drv_ver(char *buf, size_t n) * \brief Display SSC driver version after initilazation succeeds * * \return number of bytes will be printed * \ingroup IFX_SSC_INTERNAL */ static inline int ifx_ssc_drv_ver(char *buf, size_t n) { return ifx_driver_version(buf, n, "SSC", IFX_SSC_VER_MAJOR, IFX_SSC_VER_MID, IFX_SSC_VER_MINOR); } static ssize_t ifx_ssc_proc_version_show(struct seq_file *m, void *v __maybe_unused) { char buf[256]; ifx_ssc_drv_ver(buf, sizeof(buf)); seq_puts(m, buf); return 0; } static int ifx_ssc_proc_version_open(struct inode *inode __maybe_unused, struct file *file) { return single_open(file, ifx_ssc_proc_version_show, NULL); } struct proc_info { const char *name; umode_t mode; const struct file_operations fops; }; static struct proc_info proc_info[] = { { .name = "version", .fops = { .open = ifx_ssc_proc_version_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }, }, { .name = "stats", .fops = { .open = ifx_ssc_proc_stats_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }, }, { .name = "reg", .fops = { .open = ifx_ssc_proc_reg_read_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }, }, { NULL, } }; /** * \fn static void ifx_ssc_proc_create(void) * \brief Create ssc proc directory and file when module initialized. * * \return none * \ingroup IFX_SSC_INTERNAL */ static void ifx_ssc_proc_create(void) { struct proc_info *pi; ifx_ssc_proc = proc_mkdir("driver/ifx_ssc", NULL); if (!ifx_ssc_proc) { pr_err("%s:%d:%s: Unable to create proc dir 'driver/ifx_ssc'", __FILE__, __LINE__, __func__); BUG(); } for (pi = proc_info; pi->name; pi++) { struct proc_dir_entry *pe; pe = proc_create(pi->name, pi->mode, ifx_ssc_proc, &pi->fops); if (!pe) { pr_err("%s: Unable to create proc entry %s", __func__, pi->name); BUG(); } } } /** * \fn static void ifx_ssc_proc_delete(void) * \brief Delete ssc proc directory and file. * * \return none * \ingroup IFX_SSC_INTERNAL */ static void ifx_ssc_proc_delete(void) { remove_proc_subtree("driver/ifx_ssc", NULL); } /** * \fn static int __init ifx_ssc_init (void) * \brief SSC module Initialization. * * \return -ENOMEM Failed to allocate memory * \return -EBUSY Failed to iomap register space * \return 0 OK * \ingroup IFX_SSC_INTERNAL */ static int __init ifx_ssc_init(void) { u32 reg; struct ifx_ssc_port *port; int i, j, nbytes; int ret_val = -ENOMEM; char ver_str[128] = {0}; debugfs_create_bool("ifxmips_ssc_debug_fifo_op", 0666, NULL, &ifx_ssc_debug_fifo_op); debugfs_create_bool("ifxmips_ssc_debug_block_after_write_op", 0666, NULL, &ifx_ssc_debug_block_after_write_op); nbytes = IFX_SSC_MAX_PORT_NUM * sizeof(struct ifx_ssc_port); ifx_ssc_isp = (struct ifx_ssc_port *) kmalloc(nbytes, GFP_KERNEL); if (ifx_ssc_isp == NULL) { printk(KERN_ERR "%s: no memory for isp\n", __func__); return (ret_val); } memset(ifx_ssc_isp, 0, nbytes); /* set default values in ifx_ssc_port */ for (i = 0; i < IFX_SSC_MAX_PORT_NUM; i++) { port = &ifx_ssc_isp[i]; port->port_idx = i; /* default values for the HwOpts */ port->opts.abortErrDetect = IFX_SSC_DEF_ABRT_ERR_DETECT; port->opts.rxOvErrDetect = IFX_SSC_DEF_RO_ERR_DETECT; port->opts.rxUndErrDetect = IFX_SSC_DEF_RU_ERR_DETECT; port->opts.txOvErrDetect = IFX_SSC_DEF_TO_ERR_DETECT; port->opts.txUndErrDetect = IFX_SSC_DEF_TU_ERR_DETECT; port->opts.loopBack = IFX_SSC_DEF_LOOP_BACK; port->opts.echoMode = IFX_SSC_DEF_ECHO_MODE; port->opts.idleValue = IFX_SSC_DEF_IDLE_DATA; port->opts.clockPolarity = IFX_SSC_DEF_CLOCK_POLARITY; port->opts.clockPhase = IFX_SSC_DEF_CLOCK_PHASE; port->opts.headingControl = IFX_SSC_DEF_HEADING_CONTROL; port->opts.dataWidth = IFX_SSC_DEF_DATA_WIDTH; port->opts.modeRxTx = IFX_SSC_DEF_MODE_RXTX; port->opts.gpoCs = IFX_SSC_DEF_GPO_CS; port->opts.gpoInv = IFX_SSC_DEF_GPO_INV; port->opts.masterSelect = IFX_SSC_DEF_MASTERSLAVE; port->prev_ssc_clk = 0; port->baudrate = IFX_SSC_DEF_BAUDRATE; port->prev_baudrate = 0; port->prev_ssc_mode = IFX_SSC_MODE_UNKNOWN; port->ssc_ndevs = 0; port->ssc_fragSize = DEFAULT_SSC_FRAGMENT_SIZE; /* values specific to SSC */ port->mapbase = IFX_SSC_PHY_BASE; port->membase = ioremap_nocache(port->mapbase, IFX_SSC_SIZE); if (!port->membase) { printk(KERN_ERR "%s: Failed during io remap\n", __func__); ret_val = -EBUSY; goto errout1; } strcpy(port->name, IFX_SSC_NAME); port->ssc_cs_locked = IFX_FALSE; port->ssc_debug = IS_ENABLED(CONFIG_IFX_SPI_DEBUG) ? SSC_MSG_ERROR : 0; port->dma_is_in_half_duplex = 1; atomic_set(&port->dma_wait_state, 0); /* Queue initialization */ TAILQ_INIT(&port->ssc_devq); for (j = 0; j < IFX_SSC_PRIO_MAX; j++) { TAILQ_INIT(&port->ssc_syncq[j]); } IFX_SSC_Q_LOCK_INIT(port); IFX_SSC_SEM_INIT(port->dev_sem); IFX_SSC_IRQ_LOCK_INIT(port, "ifx_ssc_lock"); ifx_ssc_thread_init(port); /* Activate SSC */ SPI_PMU_SETUP(IFX_PMU_ENABLE); reg = IFX_SSC_GET_CLC(port); reg |= SM(IFX_SSC_DEF_RMC, IFX_SSC_CLC_RMC); reg &= ~IFX_SSC_CLC_DIS; IFX_SSC_SET_CLC(reg, port); port->ssc_fake_irq = IFX_SSC_FAKE_IRQ_NO; ret_val = ifx_ssc_int_wrapper.request(port->ssc_fake_irq, ifx_ssc_fake_isr, IRQF_DISABLED, "ifx_ssc_tx", port); if (ret_val) { printk(KERN_ERR "%s: unable to get irq %d\n", __func__, port->ssc_fake_irq); goto errout5; } /* Disable SSC module level real hardware interrupts */ IFX_SSC_SET_IRN_EN(0, port); /* init serial framing register */ IFX_SSC_SET_FRAMING_CON(IFX_SSC_DEF_SFCON, port); if (ifx_ssc_hwinit(port) < 0) { ifx_ssc_gpio_release(); printk(KERN_ERR "%s: hardware init failed for port %d\n", __func__, i); goto errout6; } #ifdef CONFIG_SYSCTL ifx_ssc_port_sysctl_attach(port); #endif /* CONFIG_SYSCTL */ #ifdef CONFIG_IFX_PMCU ifx_ssc_pmcu_init(port); #endif /* CONFIG_IFX_PMCU */ } ifx_ssc_proc_create(); ifx_ssc_drv_ver(ver_str, sizeof(ver_str)); printk(KERN_INFO "%s\n", ver_str); return 0; errout6: ifx_ssc_int_wrapper.free(port->ssc_fake_irq, port); errout5: iounmap(port->membase); errout1: kfree(ifx_ssc_isp); return (ret_val); } /** * \fn static void __exit ifx_ssc_exit (void) * \brief SSC Module Cleanup. * * Upon removal of the SSC module this function will free all allocated * resources and unregister devices. * \return none * \ingroup IFX_SSC_INTERNAL */ static void __exit ifx_ssc_exit (void) { int i; struct ifx_ssc_port *port; /* free up any allocated memory */ for (i = 0; i < IFX_SSC_MAX_PORT_NUM; i++) { port = &ifx_ssc_isp[i]; /* Disable the SSC */ IFX_SSC_CONFIG_MODE(port); IFX_SSC_SEM_LOCK(port->dev_sem); if (!TAILQ_EMPTY(&port->ssc_devq)) { IFX_SSC_SEM_UNLOCK(port->dev_sem); printk(KERN_ERR "%s SSC devices still attached, please release them first\n", __func__); return; } IFX_SSC_SEM_UNLOCK(port->dev_sem); ifx_ssc_int_wrapper.free(port->ssc_fake_irq, port); if (probe_kthread_data(port->ssc_kthread)) pr_err("%s: Terminating thread ifx_sscd\n", __func__); IFX_SSC_IRQ_LOCK_DESTROY(port); IFX_SSC_Q_LOCK_DESTROY(port); #ifdef CONFIG_SYSCTL ifx_ssc_port_sysctl_detach(port); #endif /* CONFIG_SYSCTL */ #ifdef CONFIG_IFX_PMCU ifx_ssc_pmcu_exit(port); #endif /* CONFIG_IFX_PMCU */ iounmap(port->membase); } ifx_ssc_gpio_release(); kfree(ifx_ssc_isp); ifx_ssc_proc_delete(); SPI_PMU_SETUP(IFX_PMU_DISABLE); } module_init (ifx_ssc_init); module_exit (ifx_ssc_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lei Chuanhua, chuanhua.lei@infineon.com"); MODULE_DESCRIPTION("IFX SSC driver"); MODULE_SUPPORTED_DEVICE("IFX SSC IP module");