//#define MULTICORE_DEBUG 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LPORT_NUM 4 #define DEVNAME "lport" #define LPORT_MAJOR 248 //#define LPORT_BUF_SIZE 0x8000 // Число должно быть кратно 16 #if !defined(CONFIG_NVCOM01M) && !defined(CONFIG_MC24R2) && !defined(CONFIG_MC24M) # define MC_CSR_MFBSP_RX MC_CSR_MFBSP # define MC_CSR_MFBSP_TX MC_CSR_MFBSP # define MC_IR_MFBSP_RX MC_IR_MFBSP # define MC_IR_MFBSP_TX MC_IR_MFBSP # define MC_RUN_MFBSP_RX MC_RUN_MFBSP # define MC_RUN_MFBSP_TX MC_RUN_MFBSP #endif // Направление порта: только прием или только передача typedef enum { MC_DIR_READ = 0, MC_DIR_WRITE = 1 } mc_direction_t; // Один буфер двойной буферизации typedef struct _io_buffer { // Непосредственно буфер (должен быть выравнен на границу 8 байт для // нормальной работы DMA) void *buf; // Адрес в программе dma_addr_t buf_phys; // Физический адрес // Текущий указатель, с которого производится чтение данных // пользователем char *user_p; // Признак пустого буфера int empty; // Признак полного буфера int full; } io_buffer; // Главная структура данных, описывающая один порт LPORT struct lport { // Номер порта LPORT (от 0 до 3) int id; // Указатель на область регистров MFBSP void __iomem *mfbsp_regs; // Указатель на область регистров TX DMA MFBSP void __iomem *dma_tx_regs; // Указатель на область регистров RX DMA MFBSP void __iomem *dma_rx_regs; // Направление порта: только прием или только передача mc_direction_t direction; // Признак неблокирующего ввода-вывода int nonblock; // Буфер для двойной буферизации при приеме io_buffer iobuf [2]; // Флаг "устройство уже используется" для защиты от повторного // открытия файла устройства int open; // Текущий номер (0 или 1) буфера, используемого функцией read // для чтения int user_buf_idx; // Текущий номер (0 или 1) буфера, используемого RX DMA для приема int dma_buf_idx; // Признак "RX DMA остановлено" int dma_stopped; // Очереди ожидания для функций read и write (в режиме // блокирующего ввода-вывода) wait_queue_head_t rxwq, txwq; // Таймер, пробуждащий заблокированную на очереди ожидания // функцию read struct timer_list rxtimer; // Признак сработавшего таймера (для снятия с точки // ожидания функции read) int rxtimer_done; //////////////// // Статистика // //////////////// // Количество принятых или переданных (в зависимости от // режима открытия) байт unsigned io_stat; // Количество принятых или переданных пакетов (входов в // функцию read или write) unsigned io_packets; // Количество ошибок при приеме (счетчик увеличивается на 1, если // была ошибка при работе RX DMA при получении одного буфера данных // длинной CONFIG_MULTICORE_LPORT_BUFSZ) unsigned rx_err; // Количество "перевзводов" RX или TX DMA unsigned dma_reinit; // Количество ожиданий готовности TX DMA в функции write // (количество блокировок в этой функции) unsigned txdma_waits; // Количество срабатываний таймера, пробуждающего функцию read unsigned rxtimer_stat; }; static struct platform_device *lport_pdev[LPORT_NUM]; static ssize_t lport_read(struct file *file, char *buf, size_t count, loff_t *ppos); static ssize_t lport_write(struct file *file, const char *buf, size_t count, loff_t *ppos); static int lport_open(struct inode *inode, struct file *file); static int lport_release(struct inode *inode, struct file *file); static long lport_ioctl (struct file *file, unsigned int cmd, unsigned long arg); // Доступные файловые операции static struct file_operations lport_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = lport_read, .write = lport_write, .open = lport_open, .release = lport_release, .compat_ioctl = lport_ioctl, .unlocked_ioctl = lport_ioctl, }; static inline u32 _R(struct lport *lpc, unsigned idx) { return __raw_readl(lpc->mfbsp_regs + idx); } static inline void _W(struct lport *lpc, unsigned idx, u32 val) { __raw_writel(val, lpc->mfbsp_regs + idx); } static inline u32 _RRX(struct lport *lpc, unsigned idx) { return __raw_readl(lpc->dma_rx_regs + idx); } static inline void _WRX(struct lport *lpc, unsigned idx, u32 val) { __raw_writel(val, lpc->dma_rx_regs + idx); } static inline u32 _RTX(struct lport *lpc, unsigned idx) { return __raw_readl(lpc->dma_tx_regs + idx); } static inline void _WTX(struct lport *lpc, unsigned idx, u32 val) { __raw_writel(val, lpc->dma_tx_regs + idx); } // // Сброс статистики // static void reset_stat (struct lport *port) { port->io_stat = 0; port->io_packets = 0; port->rxtimer_stat = 0; port->dma_reinit = 0; port->txdma_waits = 0; port->rx_err = 0; } // // Запуск TX DMA // static inline void start_tx_dma (struct lport *port, io_buffer *buf, int count) { _WTX(port, RG_DMA_IR, buf->buf_phys); _WTX(port, RG_DMA_CSR, F_DMA_WN(0) | F_DMA_WCX((count >> 3) - 1) | F_DMA_RUN); } // // Запуск RX DMA // static inline void start_rx_dma (struct lport *port, io_buffer *buf) { _WRX(port, RG_DMA_IR, buf->buf_phys); _WRX(port, RG_DMA_CSR, F_DMA_WN(0) | F_DMA_WCX((CONFIG_MULTICORE_LPORT_BUFSZ >> 3) - 1) | F_DMA_RUN); } // // Переключение RX DMA на соседний приемный буфер // (при реализации алгоритма двойной буферизации) // static void switch_rx_dma (struct lport *port) { PDEBUG ("switch_rx_dma\n"); port->dma_buf_idx = ! port->dma_buf_idx; port->iobuf [port->dma_buf_idx].empty = 0; port->dma_stopped = 0; start_rx_dma (port, &port->iobuf[port->dma_buf_idx]); port->dma_reinit++; } // // Функция, пробуждающая читателя (в режиме блокирующего // ввода-вывода). Вызывается по таймеру rxtimer // static void delay_expired (unsigned long data) { struct lport *port = (struct lport *) data; PDEBUG ("Delay expired!\n"); port->rxtimer_done = 1; port->rxtimer_stat++; // Пробуждаем ожидающих читателей wake_up_interruptible (&port->rxwq); } // // Обработка окончания текущей передачи RX DMA. Если второй приемный буфер // свободен, то RX DMA переключается на него и запускается новая передача. // Иначе канал RX DMA остается в остановленном состоянии и должен запущен // читателем (функция lport_read), когда тот освободит один буфер. // static irqreturn_t lport_dma_rx_interrupt (int cpl, void *dev_id) { struct lport *port = (struct lport *) dev_id; PDEBUG ("lport_dma_rx_interrupt!\n"); switch (port->direction) { case MC_DIR_READ: PDEBUGG ("End of DMA chain\n"); port->iobuf [port->dma_buf_idx].full = 1; if (_R(port, RG_CSR) & F_LRERR) port->rx_err++; if (port->iobuf [! port->dma_buf_idx].empty) { PDEBUG ("Reinitializing RX DMA\n"); switch_rx_dma (port); } else { PDEBUG ("No free input buffer. DMA is not reinitialized\n"); port->dma_stopped = 1; _RRX(port, RG_DMA_CSR); } break; default: BUG_ON(1); } return IRQ_HANDLED; } // // Обработка окончания текущей передачи TX DMA. Если писатель ждет // окончания текущей передачи, то данная функция снимет его с точки // ожидания. // static irqreturn_t lport_dma_tx_interrupt (int cpl, void *dev_id) { struct lport *port = (struct lport *) dev_id; PDEBUG ("lport_dma_tx_interrupt!\n"); switch (port->direction) { case MC_DIR_WRITE: // Снимаем флаг прерывания _RTX(port, RG_DMA_CSR); // Пробуждаем ожидающих писателей wake_up_interruptible (&port->txwq); break; default: BUG_ON(1); } return IRQ_HANDLED; } // // Расчет доступной для чтения информации в указанном буфере @pbuf. // Принципиально может быть две ситуации: 1) буфер заполнен (full == 1), // и тогда размер имеющихся данных рассчитывается исходя из размера // буфера и текущего указателя читателя (user_p); 2) буфер не заполнен, // значит в него идет прием каналом RX DMA, тогда размер данных // рассчитывается как разница указателя IR RX DMA и текущего // указателя читателя (user_p). // static inline unsigned calc_avail_size (struct lport *port, io_buffer *pbuf) { unsigned sz; disable_irq (mfbsp_dma_rx_irq_num (port->id)); if (pbuf->full) sz = CONFIG_MULTICORE_LPORT_BUFSZ - ((unsigned)pbuf->user_p - (unsigned)pbuf->buf); else sz = _RRX(port, RG_DMA_IR) - pbuf->buf_phys - ((unsigned)pbuf->user_p - (unsigned)pbuf->buf); PDEBUG ("avail_size = %d, full = %d\n", sz, pbuf->full); enable_irq (mfbsp_dma_rx_irq_num (port->id)); return sz; } // // Функция чтения данных из файла устройства пользователем. // static ssize_t lport_read(struct file *file, char *buf, size_t count, loff_t *ppos) { unsigned int port_num = iminor(file->f_dentry->d_inode); struct platform_device *pdev = lport_pdev[port_num]; struct lport *port = platform_get_drvdata(pdev); size_t completed = 0; unsigned avail_size, size_to_read; io_buffer *pbuf = &port->iobuf [port->user_buf_idx]; PDEBUG ("===lport_read enter, port_num = %d\n", port_num); // Разрешен прием данных только блоками, размер которых кратен 8. if (count & 7) return -EINVAL; avail_size = calc_avail_size (port, pbuf); // Если неблокирующий режим ввода-вывода и данных нет, // то сразу выходим с нулевым результатом. if (port->nonblock && avail_size == 0) return 0; // Блокирующий режим ввода-вывода. Ожидаем появления данных с помощью // механизма wait_event. Данный поток (читатель) просыпается по таймеру // и проверяет, не появились ли данные. Если да, то выполняется их выдача // пользователю, иначе поток опять засыпает на точке ожидания. while (avail_size == 0) { PDEBUGG ("avail_size == 0. Waiting for interrupt\n"); port->rxtimer_done = 0; port->rxtimer.expires = jiffies + 1; add_timer (&port->rxtimer); // Ожидаем сигнала от таймера rxtimer if (wait_event_interruptible (port->rxwq, port->rxtimer_done)) return -ERESTARTSYS; avail_size = calc_avail_size (port, pbuf); } do { // Копируем пользователю имеющиеся в текущем приемном буфере данные. size_to_read = (count < avail_size) ? count : avail_size; #ifdef MULTICORE_DEBUG if ((unsigned)buf & 3) printk("r buf @ %p\n", buf); if ((unsigned)pbuf->user_p & 3) printk("r pbuf->user_p @ %p\n", pbuf->user_p); if (size_to_read & 3) printk("r size_to_read = %d\n", size_to_read); if (completed & 3) printk("r completed = %d\n", completed); #endif if (copy_to_user (buf + completed, pbuf->user_p, size_to_read)) break; completed += size_to_read; count -= size_to_read; pbuf->user_p += size_to_read; // Если из текущего буфера вычитаны все данные, переключаем буфер if ((unsigned)pbuf->user_p - (unsigned)pbuf->buf >= CONFIG_MULTICORE_LPORT_BUFSZ) { PDEBUG ("Swapping user buffer\n"); disable_irq (mfbsp_dma_rx_irq_num (port->id)); pbuf->empty = 1; pbuf->full = 0; pbuf->user_p = pbuf->buf; if (port->dma_stopped) { switch_rx_dma (port); } enable_irq (mfbsp_dma_rx_irq_num (port->id)); port->user_buf_idx = ! port->user_buf_idx; pbuf = &port->iobuf [port->user_buf_idx]; } avail_size = calc_avail_size (port, pbuf); // Выходим, если мы скопировали пользователю весь запрошенный им размер, // либо если у нас не осталось данных. } while (count && avail_size); port->io_stat += completed; port->io_packets++; PDEBUG ("===lport_read exit, port_num = %d, completed = %d\n", port_num, completed); return completed; } static ssize_t lport_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned int port_num = iminor(file->f_dentry->d_inode); struct platform_device *pdev = lport_pdev[port_num]; struct lport *port = platform_get_drvdata(pdev); unsigned nbytes; unsigned completed = 0; char *piobuf = port->iobuf[0].buf; PDEBUGG ("lport_write enter, port_num = %d, count = %d\n", minor, count); // Разрешена передача данных только блоками, размер которых кратен 8. if (count & 7) return -EINVAL; while (count) { // Если занят канал TX DMA, ... if (_RTX(port, RG_DMA_RUN) & F_DMA_RUN) { // При неблокирующем вводе-выводе, если канал TX DMA занят, // сразу выходим и возвращаем количество байт, которое удалось // передать на текущий момент. if (port->nonblock) break; // При блокирующем вводе-выводе ждем сигнала о завершении // текущей передачи TX DMA. Сигнал приходит от обработчика // прерывания по окончанию передачи TX DMA. PDEBUG ("Waiting for TX DMA free\n"); PDEBUG ("tx dma csr = %08X\n", _RTX(port, RG_DMA_RUN)); enable_irq (mfbsp_dma_tx_irq_num (port->id)); if (wait_event_interruptible (port->txwq, (_RTX(port, RG_DMA_RUN) & F_DMA_RUN) == 0)) { disable_irq (mfbsp_dma_tx_irq_num (port->id)); return -ERESTARTSYS; } disable_irq (mfbsp_dma_tx_irq_num (port->id)); PDEBUG ("Waiting for TX DMA done\n"); port->txdma_waits++; } nbytes = count > CONFIG_MULTICORE_LPORT_BUFSZ ? CONFIG_MULTICORE_LPORT_BUFSZ : count; PDEBUGG ("nbytes = %d\n", nbytes); #ifdef MULTICORE_DEBUG if ((unsigned)buf & 3) printk("w buf @ %p\n", buf); if ((unsigned)piobuf & 3) printk("w piobuf @ %p\n", piobuf); if (nbytes & 3) printk("w nbytes = %d\n", nbytes); if (completed & 3) printk("w completed = %d\n", completed); #endif // Аккуратно принимаем данные от пользователя в свой буфер if (copy_from_user (piobuf, buf + completed, nbytes)) { port->io_packets++; return completed; } PDEBUGG ("iobuf: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", piobuf [0], piobuf [1], piobuf [2], piobuf [3], piobuf [4], piobuf[5], piobuf [6], piobuf [7], piobuf [8], piobuf [9], piobuf [10], piobuf [11], piobuf [12], piobuf [13], piobuf [14], piobuf [15]); // Начинаем выдачу start_tx_dma (port, &port->iobuf[0], nbytes); port->dma_reinit++; count -= nbytes; completed += nbytes; port->io_stat += nbytes; // При блокирующем вводе-выводе не выходим из функции, пока не // выдадим весь запрошенный размер. } PDEBUGG ("lport_write exit, port_num = %d, completed = %d\n", minor, completed); port->io_packets++; return completed; } // // Функция-обработчик команд пользователя ioctl // static long lport_ioctl (struct file *file, unsigned int cmd, unsigned long arg) { unsigned int port_num = iminor(file->f_dentry->d_inode); struct platform_device *pdev = lport_pdev[port_num]; struct lport *port = platform_get_drvdata(pdev); unsigned div; unsigned csr; csr = _R(port, RG_CSR); switch (cmd) { // Получение значения делителя частоты (текущей установки скорости // приема или передачи) case LPORT_GET_CLKDIV: div = F_GET_LCLK_RATE (csr); return (div + 1) << 1; // Установка значения делителя частоты (текущей установки скорости // приема или передачи) case LPORT_SET_CLKDIV: if (arg < 2 || arg > 64 || (arg & 1)) return -EINVAL; _W(port, RG_CSR, csr & ~F_LEN); div = (arg >> 1) - 1; PDEBUG ("new divisor %d for lport #%d\n", div, port_num); csr &= ~F_LCLK_RATE(31); csr |= F_LCLK_RATE(div) | F_LEN; _W(port, RG_CSR, csr); PDEBUG ("LCLK_RATE = %d\n", F_GET_LCLK_RATE (_R(port, RG_CSR))); return 0; // Получение значения ширины шины LPORT (4 или 8 линий) case LPORT_GET_WIDTH: if (csr & F_LDW) return 8; else return 4; // Установка значения ширины шины LPORT (4 или 8 линий) case LPORT_SET_WIDTH: if (arg != 4 && arg != 8) return -EINVAL; _W(port, RG_CSR, csr & ~F_LEN); if (arg == 4) csr &= ~F_LDW; if (arg == 8) csr |= F_LDW; _W(port, RG_CSR, csr | F_LEN); return 0; // Сброс статистики case LPORT_RESET_STAT: reset_stat (port); return 0; default: return -ENOTTY; } } // // Открытие порта // static int lport_open(struct inode *inode, struct file *file) { unsigned int port_num = iminor(inode); // получаем номер порта struct platform_device *pdev; struct lport *port; int ret; PDEBUG ("===lport_open, port_num = %d\n", port_num); if (port_num >= LPORT_NUM) return -ENXIO; pdev = lport_pdev[port_num]; PDEBUG ("pdev @ %p\n", pdev); if (port_num >= LPORT_NUM) return -ENXIO; port = platform_get_drvdata(pdev); // Запрещаем повторное открытие порта if (port->open) return -EBUSY; port->open = 1; reset_stat (port); _W(port, RG_EMERG, F_RST_LPTBUF | F_RST_TXBUF | F_RST_RXBUF); // Открытие возможно только в двух режимах: на чтение или на запись. // Открытие одновременно на чтение и на запись не допустимо. switch (file->f_flags & 0x3) { case 0: // Только чтение port->direction = MC_DIR_READ; // Инициализация буферов двойной буферизации для приема. port->user_buf_idx = port->dma_buf_idx = 0; port->iobuf[0].empty = 0; port->iobuf[1].empty = 1; port->iobuf[0].full = port->iobuf[1].full = 0; port->iobuf[0].user_p = port->iobuf[0].buf; port->iobuf[1].user_p = port->iobuf[1].buf; port->rxtimer_done = 0; // Запоминаем требуемый режим: блокирующий или неблокирующий port->nonblock = file->f_flags & O_NONBLOCK; // Запуск устройства LPORT и RX DMA port->dma_stopped = 0; ret = request_irq (mfbsp_dma_rx_irq_num (port_num), lport_dma_rx_interrupt, IRQF_DISABLED, "lport_dma", port); if (ret) { printk (KERN_ALERT "Failed to request IRQ for RX DMA!\n"); return -EBUSY; } _W(port, RG_CSR, (_R(port, RG_CSR) & ~F_LTRAN) | F_LEN); start_rx_dma (port, &port->iobuf[0]); break; case 1: // Только запись port->direction = MC_DIR_WRITE; // Запоминаем требуемый режим: блокирующий или неблокирующий port->nonblock = file->f_flags & O_NONBLOCK; // Запуск устройства LPORT и TX DMA ret = request_irq (mfbsp_dma_tx_irq_num (port_num), lport_dma_tx_interrupt, IRQF_DISABLED, "lport_dma", port); if (ret) { printk (KERN_ALERT "Failed to request IRQ for TX DMA!\n"); return -EBUSY; } disable_irq (mfbsp_dma_tx_irq_num (port_num)); _W(port, RG_CSR, _R(port, RG_CSR) | F_LTRAN | F_LEN); break; default: return -EINVAL; } PDEBUG ("NONBLOCK = %d\n", port->nonblock); return 0; } // // Закрытия порта // static int lport_release(struct inode *inode, struct file *file) { unsigned int port_num = iminor(inode); // получаем номер порта struct platform_device *pdev = lport_pdev[port_num]; struct lport *port = platform_get_drvdata(pdev); PDEBUG ("===lport_release, port_num = %d\n", port_num); switch (port->direction) { case MC_DIR_READ: disable_irq (mfbsp_dma_rx_irq_num (port->id)); free_irq (mfbsp_dma_rx_irq_num (port->id), port); break; case MC_DIR_WRITE: disable_irq (mfbsp_dma_tx_irq_num (port->id)); free_irq (mfbsp_dma_tx_irq_num (port->id), port); break; } PDEBUG ("CSR = %08X\n", _R(port, RG_CSR)); PDEBUG ("DIR = %08X\n", _R(port, RG_DIR)); PDEBUG ("GPIO_DR = %08X\n", _R(port, RG_GPIO_DR)); PDEBUG ("TCTR = %08X\n", _R(port, RG_TCTR)); PDEBUG ("RCTR = %08X\n", _R(port, RG_RCTR)); PDEBUG ("TSR = %08X\n", _R(port, RG_TSR)); PDEBUG ("RSR = %08X\n", _R(port, RG_RSR)); PDEBUG ("TCTR_RATE = %08X\n", _R(port, RG_TCTR_RATE)); PDEBUG ("RCTR_RATE = %08X\n", _R(port, RG_RCTR_RATE)); PDEBUG ("TSTART = %08X\n", _R(port, RG_TSTART)); PDEBUG ("RSTART = %08X\n", _R(port, RG_RSTART)); PDEBUG ("EMERG = %08X\n", _R(port, RG_EMERG)); PDEBUG ("IMASK = %08X\n", _R(port, RG_IMASK)); PDEBUG ("RX_CSR = %08X\n", _RRX(port, RG_DMA_CSR)); PDEBUG ("RX_CP = %08X\n", _RRX(port, RG_DMA_CP)); PDEBUG ("RX_IR = %08X\n", _RRX(port, RG_DMA_IR)); PDEBUG ("TX_CSR = %08X\n", _RTX(port, RG_DMA_CSR)); PDEBUG ("TX_CP = %08X\n", _RTX(port, RG_DMA_CP)); PDEBUG ("TX_IR = %08X\n", _RTX(port, RG_DMA_IR)); _WTX(port, RG_DMA_CSR, 0); _WRX(port, RG_DMA_CSR, 0); _W(port, RG_EMERG, F_RST_TX_DBG | F_RST_RX_DBG); while (_RRX(port, RG_DMA_CSR) & F_DMA_RUN); while (_RTX(port, RG_DMA_CSR) & F_DMA_RUN); _W(port, RG_EMERG, F_RST_LPTBUF | F_RST_TXBUF | F_RST_RXBUF); _W(port, RG_CSR, _R(port, RG_CSR) & ~MC_MFBSP_LEN); port->open = 0; return 0; } // // Печать статистики через файловую систему /proc // static int read_proc (char *buf, char **start, off_t offset, int count, int *eof, void *data) { struct lport *port = (struct lport *) data; int len = 0; len += sprintf (buf + len, "LPORT%d, direction: %s\n", port->id, (port->direction == MC_DIR_READ) ? "RX" : "TX"); len += sprintf (buf + len, "%s: %d packets, %d bytes, RX errors: %d\n", (port->direction == MC_DIR_READ) ? "Received" : "Transmitted", port->io_packets, port->io_stat, port->rx_err); len += sprintf (buf + len, "RX-timer shots: %d, DMA reinits: %d, " "TX DMA waitings: %d\n", port->rxtimer_stat, port->dma_reinit, port->txdma_waits); *eof = 1; return len; } static int chrdev_registered = 0; static __devinit int mc_lport_probe(struct platform_device *pdev) { int ret; struct resource *r; struct lport *lpc; char proc_filename [16]; if (pdev->id >= LPORT_NUM) { ret = -EINVAL; goto err_get_res; } if (!chrdev_registered) { MC_CLKEN |= MC_CLKEN_MFBSP; if (register_chrdev (LPORT_MAJOR, DEVNAME, &lport_fops)) { printk (DEVNAME ": unable to get char major %d\n", LPORT_MAJOR); ret = -ENODEV; goto err_get_res; } memset(lport_pdev, 0, sizeof(lport_pdev)); chrdev_registered = 1; } lpc = devm_kzalloc(&pdev->dev, sizeof(struct lport), GFP_KERNEL); if (!lpc) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } lpc->mfbsp_regs = devm_request_and_ioremap(&pdev->dev, r); if (!lpc->mfbsp_regs) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txdma"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } lpc->dma_tx_regs = devm_request_and_ioremap(&pdev->dev, r); if (!lpc->dma_tx_regs) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxdma"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } lpc->dma_rx_regs = devm_request_and_ioremap(&pdev->dev, r); if (!lpc->dma_rx_regs) { ret = -ENOMEM; goto err_get_res; } lpc->iobuf[0].buf = dmam_alloc_coherent(&pdev->dev, CONFIG_MULTICORE_LPORT_BUFSZ, &lpc->iobuf[0].buf_phys, GFP_KERNEL); if (!lpc->iobuf[0].buf) { ret = -ENOMEM; goto err_get_res; } lpc->iobuf[1].buf = dmam_alloc_coherent(&pdev->dev, CONFIG_MULTICORE_LPORT_BUFSZ, &lpc->iobuf[1].buf_phys, GFP_KERNEL); if (!lpc->iobuf[1].buf) { ret = -ENOMEM; goto err_get_res; } lpc->id = pdev->id; lpc->open = 0; init_waitqueue_head (&lpc->rxwq); init_waitqueue_head (&lpc->txwq); init_timer (&lpc->rxtimer); lpc->rxtimer.function = delay_expired; lpc->rxtimer.data = (unsigned long) lpc; platform_set_drvdata(pdev, lpc); lport_pdev[pdev->id] = pdev; sprintf (proc_filename, "lport%d_stat", lpc->id); create_proc_read_entry (proc_filename, 0 /* default mode */, NULL /* parent dir */, read_proc, lpc /* client data */); _W(lpc, RG_CSR, F_LCLK_RATE(1) | F_LDW); return 0; err_get_res: return ret; } static __devexit int mc_lport_remove(struct platform_device *pdev) { struct lport *port = platform_get_drvdata(pdev); if (! port->open) return 0; switch (port->direction) { case MC_DIR_READ: disable_irq (mfbsp_dma_rx_irq_num (port->id)); free_irq (mfbsp_dma_rx_irq_num (port->id), port); break; case MC_DIR_WRITE: disable_irq (mfbsp_dma_tx_irq_num (port->id)); free_irq (mfbsp_dma_tx_irq_num (port->id), port); break; } _W(port, RG_RSTART, 0); _W(port, RG_TSTART, 0); _W(port, RG_CSR, _R(port, RG_CSR) & ~MC_MFBSP_LEN); port->open = 0; return 0; } static struct platform_driver mc_lport_driver = { .driver = { .name = "mc_lport", .owner = THIS_MODULE, }, .probe = mc_lport_probe, .remove = __devexit_p(mc_lport_remove), }; module_platform_driver(mc_lport_driver); MODULE_DESCRIPTION("Multicore Link Port Controller driver"); MODULE_AUTHOR("Dmitry Podkhvatilin"); MODULE_LICENSE("GPL");