//#define MULTICORE_DEBUG 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVNAME "spw" #define SPW_MAJOR 249 #if defined(CONFIG_MC24R2)||defined(CONFIG_MC24M) # define NB_OF_CHANNELS 2 #elif defined(CONFIG_MCT02R) # define NB_OF_CHANNELS 4 #elif defined(CONFIG_MCT03P) # define NB_OF_CHANNELS 4 #elif defined(CONFIG_MC30SF6) # define NB_OF_CHANNELS 2 #endif #define EOP 1 #define EEP 2 #define SWIC_RESET 0x00000079 #define SWIC_START 0x00000101 #define SWIC_RESET_ALL_IRQS 0x0001d00f #define SPW_RX_DATA_BUFSZ 65536 #define SPW_RX_DESC_BUFSZ 384 #define SPW_TX_DATA_BUFSZ 2048 #define SPW_ONE_BUFFER_DESC_CNT (SPW_RX_DESC_BUFSZ / 48) // Макросы для вычисления номеров прерываний #define IRQ_SHIFT 3 #define spw_err_irq(port_id) (IRQ_SW0_ERR + ((port_id) << IRQ_SHIFT)) #define spw_time_irq(port_id) (IRQ_SW0_TIME + ((port_id) << IRQ_SHIFT)) #define spw_link_irq(port_id) (IRQ_SW0_LINK + ((port_id) << IRQ_SHIFT)) #define spw_rx_data_irq(port_id) (IRQ_SW0_RX_DATA + ((port_id) << IRQ_SHIFT)) #define spw_rx_desc_irq(port_id) (IRQ_SW0_RX_DESC + ((port_id) << IRQ_SHIFT)) #define spw_tx_data_irq(port_id) (IRQ_SW0_TX_DATA + ((port_id) << IRQ_SHIFT)) #define spw_tx_desc_irq(port_id) (IRQ_SW0_TX_DESC + ((port_id) << IRQ_SHIFT)) // Доступ к регистрам любого канала SWIC DMA по его адресу #define SWIC_DMA_CSR(addr) MC_R ((addr) + 0x0) // Регистр управления и состояния канала #define SWIC_DMA_CP(addr) MC_R ((addr) + 0x4) // Регистр указателя цепочки канала #define SWIC_DMA_IR(addr) MC_R ((addr) + 0x8) // Индексный регистр внешней памяти канала #define SWIC_DMA_RUN(addr) MC_R ((addr) + 0xC) // Псевдорегистр управления состоянием бита RUN // Адреса каналов DMA #if defined(CONFIG_MC24R2) || defined(CONFIG_MC24M) # define SWIC_RX_DESC_CHAN(port) (0x5800 + ((port) << 12)) # define SWIC_RX_DATA_CHAN(port) (0x5840 + ((port) << 12)) # define SWIC_TX_DESC_CHAN(port) (0x5880 + ((port) << 12)) # define SWIC_TX_DATA_CHAN(port) (0x58C0 + ((port) << 12)) #elif defined(CONFIG_MCT02R) || defined(CONFIG_MCT03P) # define SWIC_RX_DESC_CHAN(port) (0xC800 + ((port) << 12)) # define SWIC_RX_DATA_CHAN(port) (0xC840 + ((port) << 12)) # define SWIC_TX_DESC_CHAN(port) (0xC880 + ((port) << 12)) # define SWIC_TX_DATA_CHAN(port) (0xC8C0 + ((port) << 12)) #endif // Формат дескриптора пакета typedef struct __attribute__((__packed__)) _spw_descriptor_t { unsigned size : 25; // Размер данных unsigned unused : 4; // Не используется unsigned type : 2; // Тип конца пакета: EOP или EEP unsigned valid : 1; // Признак заполнения дескриптора // действительными данными unsigned padding; // Дополнительные неиспользуемые 4 байта, // т.к. DMA передаёт 8-байтовыми словами. } spw_descriptor_t; // Один буфер двойной буферизации typedef struct _io_buffer { char * buf; // Непосредственно буфер (должен быть выравнен на границу 8 байт для // нормальной работы DMA) char * reader_p; // Текущий указатель, с которого производится чтение int empty; // Признак пустого буфера unsigned chain_addr; // Адрес цепочки инициализации (физ.), если она используется, иначе 0 } one_buffer; // Двойной буфер typedef struct _double_buffer { one_buffer half [2]; // Две половины буфера int size; // Размер одной половины int reader_idx; // Индекс читателя int dma_idx; // Индекс DMA int dma_chan; // Канал DMA } double_buffer; // Элемент цепочки DMA typedef struct __attribute__((__packed__)) _dma_params_t { uint32_t zero; // Неиспользуемое поле uint32_t ir; // Значение, загружаемое в IR uint32_t cp; // Значение, загружаемое в CP uint32_t csr; // Значение, загружаемое в CSR } dma_params_t; struct _spw_t { int port; // Номер канала spacewire, считая от 0 unsigned speed; // Рабочая скорость канала int open; // Признак открытия канала int connected; int nonblock; // Признак неблокирующего ввода-вывода char name [sizeof (DEVNAME) + 1]; // Имя устройства spw_descriptor_t * tx_desc; // Указатель на буфер с дескриптором передаваемого пакета char * tx_data_buffer; // Указатель на буфер с данными передаваемого пакета double_buffer rx_desc_buf; // Двойной буфер приёма дескрипторов double_buffer rx_data_buf; // Двойной буфер приёма данных dma_params_t * rx_desc_chain [2]; // Цепочки DMA для приёма дескрипторов wait_queue_head_t stwq, rxdatawq, rxdescwq, txwq; // Очереди ожидания для функций старта, read и write (в режиме блокирующего ввода-вывода) // Буфер для дескриптора передаваемого пакета spw_descriptor_t txdescbuf __attribute__ ((aligned (8))); // Буфер для передаваемого пакета char txbuf [SPW_TX_DATA_BUFSZ] __attribute__ ((aligned (8))); // Буфер для принятых дескрипторов RX DESC DMA spw_descriptor_t rxdescbuf [2][SPW_ONE_BUFFER_DESC_CNT] __attribute__ ((aligned (8))); dma_params_t rxdescchain [2][SPW_ONE_BUFFER_DESC_CNT] __attribute__ ((aligned (8))); // Буфер для принятых данных RX DATA DMA char rxdatabuf [2][SPW_RX_DATA_BUFSZ / 2] __attribute__ ((aligned (8))); // Статистика unsigned rx_eop; // Количество принятых пакетов с EOP unsigned rx_eep; // Количество принятых пакетов с EEP unsigned rx_bytes; // Количество принятых байт unsigned tx_packets; // Количество переданных пакетов unsigned tx_bytes; // Количество переданных байт unsigned txdma_waits; // Количество ожиданий освобождения DMA передатчика }; typedef struct _spw_t spw_t; spw_t spw_channel [NB_OF_CHANNELS]; struct class *spw_class; // Класс устройств SPW // Прототипы функций-обработчиков файловых операций static ssize_t spw_read(struct file *file, char *buf, size_t count, loff_t *ppos); static ssize_t spw_write(struct file *file, const char *buf, size_t count, loff_t *ppos); static int spw_open(struct inode *inode, struct file *file); static int spw_close(struct inode *inode, struct file *file); static long spw_ioctl (struct file *file, unsigned int cmd, unsigned long arg); // Доступные файловые операции static struct file_operations spw_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = spw_read, .write = spw_write, .open = spw_open, .release = spw_close, .compat_ioctl = spw_ioctl, .unlocked_ioctl = spw_ioctl, }; static inline int spw_connected (spw_t *u) { return ((MC_SWIC_STATUS (u->port) & 0x30E0) == 0x30A0); } static inline unsigned size_to_end_of_cur_buffer (double_buffer *pdbuf) { one_buffer *pcur = &pdbuf->half[pdbuf->reader_idx]; return (pdbuf->size - (pcur->reader_p - pcur->buf)); } // // Расчет доступной для чтения информации в указанном буфере @pbuf. // static inline unsigned avail_size (double_buffer *pdbuf) { one_buffer *pcur = &pdbuf->half[pdbuf->reader_idx]; int sz = SWIC_DMA_IR (pdbuf->dma_chan) - mips_virt_to_phys ((unsigned) pcur->reader_p); PDEBUG ("avail_size: sz = %d, DMA CSR = %08X, IR = %08X, addr = %08X\n", sz, SWIC_DMA_CSR (pdbuf->dma_chan), SWIC_DMA_IR (pdbuf->dma_chan), mips_virt_to_phys ((unsigned) pcur->reader_p)); if (sz >= 0) return sz; else return (sz + SPW_RX_DATA_BUFSZ); } // // Запуск канала DMA для указанного двойного буфера // static inline void start_dma (double_buffer *pdbuf) { one_buffer *pcur; PDEBUGG ("start_dma 0x%04X, dma_idx = %d\n", pdbuf->dma_chan, pdbuf->dma_idx); pcur = &pdbuf->half[pdbuf->dma_idx]; pcur->empty = 0; PDEBUGG ("pcur->chain_addr = %08X\n", pcur->chain_addr); if (pcur->chain_addr) { PDEBUGG ("First CSR in chain: %08X\n", ((dma_params_t *) pcur->chain_addr)->csr); SWIC_DMA_CP (pdbuf->dma_chan) = mips_virt_to_phys (pcur->chain_addr) | 1; } else { PDEBUGG ("pcur->buf = %08X\n", pcur->buf); SWIC_DMA_IR (pdbuf->dma_chan) = mips_virt_to_phys ((unsigned) pcur->buf); SWIC_DMA_CSR (pdbuf->dma_chan) = MC_DMA_CSR_WN(0) | MC_DMA_CSR_IM | MC_DMA_CSR_WCX((pdbuf->size >> 3) - 1) | MC_DMA_CSR_RUN; } } // // Запуск DMA, если соблюдены необходимые условия // static void start_rx_dma_if_needed (double_buffer *pdbuf) { PDEBUGG ("start_rx_dma_if_needed, SWIC_DMA_RUN(0x%04X) = %08X, second empty: %d, dma idx: %d, rd idx: %d\n", pdbuf->dma_chan, SWIC_DMA_RUN (pdbuf->dma_chan), pdbuf->half[!pdbuf->dma_idx].empty, pdbuf->dma_idx, pdbuf->reader_idx); if ( !(SWIC_DMA_RUN (pdbuf->dma_chan) & 1) // Если канал DMA сейчас остановлен && pdbuf->half[!pdbuf->dma_idx].empty) // и соседний буфер пуст, то { SWIC_DMA_CSR (pdbuf->dma_chan); // Переключаем DMA на приём в соседний буфер pdbuf->dma_idx = !pdbuf->dma_idx; start_dma (pdbuf); } } // // Продвижение указателя читателя в двойном буфере // static void move_reader_p (double_buffer *pdbuf, int distance) { one_buffer *pcur = &pdbuf->half[pdbuf->reader_idx]; int size_to_end = size_to_end_of_cur_buffer (pdbuf); int aligned_distance = (distance + 7) & ~7; PDEBUGG ("move_reader_p, distance = %d, size_to_end = %d\n", aligned_distance, size_to_end); if (aligned_distance < size_to_end) pcur->reader_p += aligned_distance; else { pcur->empty = 1; pdbuf->reader_idx = !pdbuf->reader_idx; pcur = &pdbuf->half[pdbuf->reader_idx]; pcur->reader_p = pcur->buf + (aligned_distance - size_to_end); if (pcur->chain_addr) { start_rx_dma_if_needed (pdbuf); } } } // // Обработчик прерывания по завершению приёма данных // static irqreturn_t spw_dma_rx_data_ih (int irq, void *dev_id) { spw_t *u = dev_id; // Снимаем признак прерывания MC_SWIC_RX_DATA_CSR (u->port); start_rx_dma_if_needed (&u->rx_data_buf); wake_up_interruptible (&u->rxdatawq); return IRQ_HANDLED; } // // Обработчик прерывания по приёму дескриптора // static irqreturn_t spw_dma_rx_desc_ih (int irq, void *dev_id) { spw_t *u = dev_id; // Снимаем признак прерывания MC_SWIC_RX_DESC_CSR (u->port); wake_up_interruptible (&u->rxdescwq); return IRQ_HANDLED; } // // Обработчик прерывания по завершению выдачи данных // static irqreturn_t spw_dma_tx_data_ih (int irq, void *dev_id) { spw_t *u = dev_id; // Снимаем признак прерывания MC_SWIC_TX_DATA_CSR (u->port); wake_up_interruptible (&u->txwq); return IRQ_HANDLED; } static void spw_start (spw_t *u) { u->connected = 0; // Включение частоты MC_CLKEN |= MC_CLKEN_SWIC(u->port); // Сброс контроллера MC_SWIC_MODE_CR(u->port) = SWIC_RESET; // Запись тайминга для того, чтобы SWIC отрабатывал временные интер- // валы в соответствии со стандартом SpaceWire //MC_SWIC_MODE_CR(u->port) = MC_SWIC_TIMING_WR_EN; //MC_SWIC_TX_SPEED(u->port) = MC_SWIC_TIMING(KHZ/10000); MC_SWIC_MODE_CR(u->port) = SWIC_START; // Начальные установки и пуск MC_SWIC_TX_SPEED(u->port) = MC_SWIC_TX_SPEED_PRM(CONFIG_MULTICORE_SWIC_START_SPEED / 5) | MC_SWIC_PLL_TX_EN | MC_SWIC_LVDS_EN; //MC_SWIC_TX_SPEED(u->port) = MC_SWIC_TX_SPEED_CON(SPW_START_SPEED / 5) | // MC_SWIC_TX_SPEED_PRM(u->speed / 5) | MC_SWIC_PLL_TX_EN | MC_SWIC_LVDS_EN; //mdelay (20); PDEBUG ("TX_SPEED(%d) = %08X\n", u->port, MC_SWIC_TX_SPEED(u->port)); MC_SWIC_MODE_CR(u->port) = MC_SWIC_LinkStart | MC_SWIC_AutoStart | MC_SWIC_WORK_TYPE | MC_SWIC_LINK_MASK; //MC_SWIC_MODE_CR(u->port) = MC_SWIC_LinkStart | MC_SWIC_AutoStart | // MC_SWIC_WORK_TYPE | MC_SWIC_LINK_MASK | MC_SWIC_AUTO_TX_SPEED; // Сброс всех признаков прерываний MC_SWIC_STATUS(u->port) = SWIC_RESET_ALL_IRQS; // Сброс счетчиков принятых пакетов MC_SWIC_CNT_RX_PACK(u->port) = 0; MC_SWIC_CNT_RX0_PACK(u->port) = 0; } static void spw_stop (spw_t *u) { PDEBUGG ("=== spw_stop: channel %d\n", u->port); MC_SWIC_RX_DATA_RUN (u->port) = 0; MC_SWIC_RX_DESC_RUN (u->port) = 0; MC_SWIC_TX_DATA_RUN (u->port) = 0; MC_SWIC_TX_DESC_RUN (u->port) = 0; MC_SWIC_MODE_CR (u->port) = MC_SWIC_LinkDisabled | SWIC_RESET; } void spw_set_tx_speed (spw_t *u, unsigned mbit_per_sec) { u->speed = mbit_per_sec; MC_SWIC_TX_SPEED (u->port) = (MC_SWIC_TX_SPEED (u->port) & ~MC_SWIC_TX_SPEED_PRM_MASK) | MC_SWIC_TX_SPEED_PRM (mbit_per_sec / 5); } // // Функция чтения пользователем одного принятого пакета // static ssize_t spw_read (struct file *file, char *buf, size_t size, loff_t *ppos) { unsigned int minor = iminor(file->f_dentry->d_inode); spw_t *u = &spw_channel [minor]; unsigned sz_to_end, nbytes, rest, completed; char *pdata; spw_descriptor_t *pdesc = (spw_descriptor_t *) u->rx_desc_buf.half[u->rx_desc_buf.reader_idx].reader_p; PDEBUG ("=== spw_read enter, channel = %d\n", u->port); if (! spw_connected (u)) { PDEBUG ("spw_read: channel %d not connected, wait for connection\n", u->port); //MC_SWIC_MODE_CR(u->port) |= MC_SWIC_LINK_MASK; spw_start (u); if (u->nonblock) { return 0; } if (wait_event_interruptible (u->stwq, u->connected)) { return -ERESTARTSYS; } PDEBUG ("spw_read: channel %d, waiting for connection finished\n", u->port); } while (1) { if (!pdesc->valid) { // Нет принятых дескрипторов PDEBUGG ("=== spw_read, channel = %d, no data\n", u->port); // Запускаем RX DMA, если он ещё не запущен. start_rx_dma_if_needed (&u->rx_desc_buf); disable_irq (spw_rx_data_irq (u->port)); start_rx_dma_if_needed (&u->rx_data_buf); enable_irq (spw_rx_data_irq (u->port)); // Если неблокирующий режим ввода-вывода и данных нет, // то сразу выходим с нулевым результатом. if (u->nonblock) return 0; // Если блокирующий режим, то дожидаемся получения // пакета без ошибок while (!pdesc->valid) { PDEBUGG ("=== spw_read, channel = %d, waiting for a packet\n", u->port); PDEBUG ("RX DESC DMA CSR = %08X, RX DATA DMA CSR = %08X\n", SWIC_DMA_RUN (u->rx_desc_buf.dma_chan), SWIC_DMA_RUN (u->rx_data_buf.dma_chan)); //mutex_wait (&u->rx_desc_lock); enable_irq (spw_rx_desc_irq (u->port)); if (wait_event_interruptible (u->rxdescwq, pdesc->valid)) { disable_irq (spw_rx_desc_irq (u->port)); return -ERESTARTSYS; } disable_irq (spw_rx_desc_irq (u->port)); SWIC_DMA_CSR (u->rx_desc_buf.dma_chan); // Сброс признака прерывания PDEBUGG ("=== spw_read, channel = %d, waiting done\n", u->port); } } pdesc->valid = 0; if (pdesc->type == EOP) { break; } else { // Получен дескриптор с ошибкой, ждём следующего дескриптора u->rx_eep++; move_reader_p (&u->rx_desc_buf, sizeof (spw_descriptor_t)); pdesc = (spw_descriptor_t *) u->rx_desc_buf.half[u->rx_desc_buf.reader_idx].reader_p; disable_irq (spw_rx_data_irq (u->port)); move_reader_p (&u->rx_data_buf, pdesc->size); enable_irq (spw_rx_data_irq (u->port)); } } PDEBUG ("=== spw_read, processing packet\n"); completed = 0; rest = pdesc->size; if (rest > size) { // Ошибка: пользователь предоставил маленький буфер disable_irq (spw_rx_data_irq (u->port)); move_reader_p (&u->rx_data_buf, rest); enable_irq (spw_rx_data_irq (u->port)); move_reader_p (&u->rx_desc_buf, sizeof (spw_descriptor_t)); return -EINVAL; } disable_irq (spw_rx_data_irq (u->port)); while (rest > 0) { pdata = u->rx_data_buf.half[u->rx_data_buf.reader_idx].reader_p; PDEBUG ("pdata = %08X\n", pdata); do nbytes = avail_size (&u->rx_data_buf); while (nbytes == 0); PDEBUG ("avail_size = %d, rest = %d\n", nbytes, rest); sz_to_end = size_to_end_of_cur_buffer (&u->rx_data_buf); enable_irq (spw_rx_data_irq (u->port)); if (nbytes > sz_to_end) nbytes = sz_to_end; if (nbytes > rest) nbytes = rest; copy_to_user (buf, pdata, nbytes); rest -= nbytes; completed += nbytes; disable_irq (spw_rx_data_irq (u->port)); move_reader_p (&u->rx_data_buf, nbytes); } enable_irq (spw_rx_data_irq (u->port)); move_reader_p (&u->rx_desc_buf, sizeof (spw_descriptor_t)); u->rx_eop++; u->rx_bytes += completed; PDEBUG ("=== spw_read exit, channel = %d, completed = %d\n", u->port, completed); return completed; } static ssize_t spw_write (struct file *file, const char *buf, size_t size, loff_t *ppos) { unsigned minor = iminor(file->f_dentry->d_inode); spw_t *u = &spw_channel [minor]; unsigned nbytes, completed = 0; PDEBUG ("=== spw_write enter, channel = %d, size = %d\n", u->port, size); if (! spw_connected (u)) { PDEBUG ("spw_write: channel %d not connected, wait for connection\n", u->port); //MC_SWIC_MODE_CR(u->port) |= MC_SWIC_LINK_MASK; spw_start (u); if (u->nonblock) { return 0; } if (wait_event_interruptible (u->stwq, u->connected)) { return -ERESTARTSYS; } PDEBUG ("spw_write: channel %d, waiting for connection finished\n", u->port); } PDEBUGG ("MASKR2 = %08X, prev tx_desc = %08X\n", MC_MASKR2, *((unsigned *) u->tx_desc)); // Если занят канал TX DMA, ... while (MC_SWIC_TX_DATA_RUN (u->port) & 1) { // При неблокирующем вводе-выводе, если канал TX DMA занят, сразу выходим if (u->nonblock) { return 0; } // При блокирующем вводе-выводе ждем сигнала о завершении // текущей передачи TX DMA. Сигнал приходит от обработчика // прерывания по окончанию передачи TX DMA. PDEBUGG ("Waiting for TX DATA DMA free, channel %d\n", u->port); PDEBUGG ("RX DATA CSR = %08X\n", MC_SWIC_RX_DATA_RUN (!u->port)); enable_irq (spw_tx_data_irq (u->port)); if (wait_event_interruptible (u->txwq, !(MC_SWIC_TX_DATA_RUN (u->port) & 1))) { disable_irq (spw_tx_data_irq (u->port)); return -ERESTARTSYS; } disable_irq (spw_tx_data_irq (u->port)); PDEBUGG ("Waiting for TX DMA done\n"); u->txdma_waits++; } // Настраиваем DMA на выдачу пакета u->tx_desc->size = size; u->tx_desc->type = EOP; u->tx_desc->valid = 1; u->tx_desc->unused = 0; MC_SWIC_TX_DESC_IR(u->port) = mips_virt_to_phys ((unsigned) u->tx_desc); // Адрес начала буфера MC_SWIC_TX_DESC_CSR(u->port) = MC_DMA_CSR_WCX(0) | MC_DMA_RUN; // 1 8-байтовое слово while (size > 0) { nbytes = size; if (nbytes > SPW_TX_DATA_BUFSZ) nbytes = SPW_TX_DATA_BUFSZ; copy_from_user (u->tx_data_buffer, buf + completed, nbytes); // Приходится копировать из-за проблем в DMA MC_SWIC_TX_DATA_IR(u->port) = mips_virt_to_phys ((unsigned) u->tx_data_buffer); PDEBUGG ("tp1, TX_DATA_CSR = %08X, TX_DESC_CSR = %08X, channel %d\n", MC_SWIC_TX_DATA_RUN (u->port), MC_SWIC_TX_DESC_RUN (u->port), u->port); PDEBUGG ("transmit: TX_DATA_IR = %08X, TX_DESC_IR = %08X, channel %d\n", MC_SWIC_TX_DATA_IR (u->port), MC_SWIC_TX_DESC_IR (u->port), u->port); MC_SWIC_TX_DATA_CSR(u->port) = MC_DMA_CSR_IM | MC_DMA_CSR_WCX(((nbytes + 7) >> 3) - 1) | MC_DMA_RUN; PDEBUGG ("tp2, TX_DATA_CSR = %08X, TX_DESC_CSR = %08X, channel %d\n", MC_SWIC_TX_DATA_RUN (u->port), MC_SWIC_TX_DESC_RUN (u->port), u->port); completed += nbytes; size -= nbytes; if (size > 0) { enable_irq (spw_tx_data_irq (u->port)); if (wait_event_interruptible (u->txwq, !(MC_SWIC_TX_DATA_RUN (u->port) & 1))) { disable_irq (spw_tx_data_irq (u->port)); return -ERESTARTSYS; } disable_irq (spw_tx_data_irq (u->port)); } } u->tx_packets++; u->tx_bytes += completed; PDEBUG ("=== spw_write exit, channel = %d, completed = %d\n", u->port, completed); return completed; } // // Обработчик прерывания по установке соединения // static irqreturn_t spw_connected_ih (int irq, void *dev_id) { spw_t *u = dev_id; PDEBUG ("Connected, channel: %d, STATUS = %08X\n", u->port, MC_SWIC_STATUS (u->port)); if (MC_SWIC_STATUS (u->port) & 0xE) { MC_SWIC_STATUS (u->port) |= 0xF; PDEBUG ("Error detected, waiting reconnection\n"); return IRQ_HANDLED; } MC_SWIC_STATUS (u->port) |= MC_SWIC_CONNECTED; if ( !(SWIC_DMA_RUN (u->rx_desc_buf.dma_chan))) start_dma (&u->rx_desc_buf); if ( !(SWIC_DMA_RUN (u->rx_data_buf.dma_chan))) start_dma (&u->rx_data_buf); spw_set_tx_speed (u, u->speed); wake_up_interruptible (&u->stwq); // Закрываем прерывание, иначе оно будет возникать по приему каждого пакета MC_SWIC_MODE_CR(u->port) &= ~MC_SWIC_LINK_MASK; u->connected = 1; return IRQ_HANDLED; } // // Функция-обработчик команд пользователя ioctl // static long spw_ioctl (struct file *file, unsigned int cmd, unsigned long arg) { unsigned int chan_num = iminor(file->f_dentry->d_inode); spw_t *u = &spw_channel [chan_num]; unsigned freq_mult_mhz; switch (cmd) { // Получение значения частоты приема case SPW_GET_RX_SPEED: freq_mult_mhz = mc_frequency_multiplier() * MC_QUARTZ_CLOCK_FREQ; return ((MC_SWIC_RX_SPEED (u->port) * freq_mult_mhz / 1000000) >> 7); // Получение значения частоты передачи case SPW_GET_TX_SPEED: return (MC_SWIC_TX_SPEED (u->port) & MC_SWIC_TX_SPEED_PRM_MASK) * 5; // Установка значения частоты передачи case SPW_SET_TX_SPEED: if (arg < 5 || arg > 400) return (-EINVAL); spw_set_tx_speed (u, arg); return 0; // Получение количества успешно принятых пакетов (EOP) case SPW_GET_STAT_RX_EOP: return u->rx_eop; // Получение количества принятых пакетов с ошибкой (EEP) case SPW_GET_STAT_RX_EEP: return u->rx_eep; // Получение количества принятых байт case SPW_GET_STAT_RX_BYTES: return u->rx_bytes; // Получение количества переданных пакетов case SPW_GET_STAT_TX_PACKETS: return u->tx_packets; // Получение количества переданных байт case SPW_GET_STAT_TX_BYTES: return u->tx_bytes; // Получение количества ожиданий освобождения DMA передачи case SPW_GET_STAT_TX_DMA_WAITS: return u->txdma_waits; // Сброс статистики case SPW_RESET_STAT: u->rx_eop = 0; u->rx_eep = 0; u->rx_bytes = 0; u->tx_packets = 0; u->tx_bytes = 0; u->txdma_waits = 0; return 0; default: return -ENOTTY; } } static void spw_struct_init (spw_t *u, int port, int speed) { int i, j; PDEBUGG ("spw_struct_init, port = %d\n", port); memset (u, 0, sizeof (spw_t)); u->port = port; u->speed = speed; // Инициализация буферов (двойной буферизации) принятых дескрипторов u->rx_desc_buf.size = SPW_ONE_BUFFER_DESC_CNT * sizeof (spw_descriptor_t); u->rx_desc_buf.dma_chan = SWIC_RX_DESC_CHAN (port); u->rx_desc_buf.half[0].empty = 1; u->rx_desc_buf.half[1].empty = 1; u->rx_desc_buf.half[0].reader_p = u->rx_desc_buf.half[0].buf = (char *) u->rxdescbuf[0]; u->rx_desc_buf.half[1].reader_p = u->rx_desc_buf.half[1].buf = (char *) u->rxdescbuf[1]; u->rx_desc_chain[0] = u->rxdescchain[0]; u->rx_desc_chain[1] = u->rxdescchain[1]; memset (u->rx_desc_buf.half[0].buf, 0, u->rx_desc_buf.size); memset (u->rx_desc_buf.half[1].buf, 0, u->rx_desc_buf.size); // Инициализация цепочек самоинициализации RX DESC DMA для каждого // приёмного буфера двойной буферизации for (j = 0; j < 2; ++j) { for (i = 0; i < SPW_ONE_BUFFER_DESC_CNT; ++i) { u->rx_desc_chain[j][i].ir = mips_virt_to_phys ( (unsigned) (u->rx_desc_buf.half[j].buf + i * sizeof (spw_descriptor_t))); u->rx_desc_chain[j][i].csr = MC_DMA_CSR_IM | MC_DMA_CSR_CHEN | MC_DMA_CSR_WN(0) | MC_DMA_CSR_WCX(0) | MC_DMA_CSR_RUN; u->rx_desc_chain[j][i].cp = mips_virt_to_phys ((unsigned) &u->rx_desc_chain[j][i + 1]); } u->rx_desc_chain[j][i - 1].csr = MC_DMA_CSR_IM | MC_DMA_CSR_WN(0) | MC_DMA_CSR_WCX(0) | MC_DMA_CSR_RUN; u->rx_desc_chain[j][i - 1].cp = 0; u->rx_desc_buf.half[j].chain_addr = (unsigned) u->rx_desc_chain[j]; } PDEBUGG ("CSR in chain: %08X, chain addr = %08X\n", ((dma_params_t *) u->rx_desc_buf.half[0].chain_addr)->csr, u->rx_desc_buf.half[0].chain_addr); // Инициализация буферов (двойной буферизации) принятых данных u->rx_data_buf.size = (SPW_RX_DATA_BUFSZ / 2 + 7) & ~7; u->rx_data_buf.dma_chan = SWIC_RX_DATA_CHAN (port); u->rx_data_buf.half[0].empty = 1; u->rx_data_buf.half[1].empty = 1; u->rx_data_buf.half[0].reader_p = u->rx_data_buf.half[0].buf = (char *) u->rxdatabuf[0]; u->rx_data_buf.half[1].reader_p = u->rx_data_buf.half[1].buf = (char *) u->rxdatabuf[1]; memset (u->rx_data_buf.half[0].buf, 0, u->rx_data_buf.size); memset (u->rx_data_buf.half[1].buf, 0, u->rx_data_buf.size); u->tx_desc = &u->txdescbuf; u->tx_data_buffer = u->txbuf; init_waitqueue_head (&u->stwq); init_waitqueue_head (&u->rxdescwq); init_waitqueue_head (&u->rxdatawq); init_waitqueue_head (&u->txwq); } static int spw_open (struct inode *inode, struct file *file) { unsigned chan_num = iminor(inode); // получаем номер канала SpW spw_t *u = &spw_channel [chan_num]; int ret; PDEBUG ("===spw_open, channel = %d\n", chan_num); if (chan_num >= NB_OF_CHANNELS) return -ENXIO; // Запрещаем повторное открытие порта if (u->open) return -EBUSY; spw_struct_init (u, chan_num, CONFIG_MULTICORE_SWIC_DEF_WORK_SPEED); u->open = 1; u->nonblock = file->f_flags & O_NONBLOCK; ret = request_irq (spw_rx_desc_irq (u->port), spw_dma_rx_desc_ih, 0 /*irq_flags*/, u->name, u); if (ret) return -EFAULT; ret = request_irq (spw_rx_data_irq (u->port), spw_dma_rx_data_ih, 0 /*irq_flags*/, u->name, u); if (ret) { free_irq (spw_rx_desc_irq (u->port), u); return -EFAULT; } ret = request_irq (spw_tx_data_irq (u->port), spw_dma_tx_data_ih, 0 /*irq_flags*/, u->name, u); if (ret) { free_irq (spw_rx_desc_irq (u->port), u); free_irq (spw_rx_data_irq (u->port), u); return -EFAULT; } ret = request_irq (spw_link_irq (u->port), spw_connected_ih, 0, u->name, u); if (ret) { free_irq (spw_rx_desc_irq (u->port), u); free_irq (spw_rx_data_irq (u->port), u); free_irq (spw_tx_data_irq (u->port), u); return -EFAULT; } disable_irq (spw_rx_desc_irq (u->port)); disable_irq (spw_tx_data_irq (u->port)); PDEBUG ("MASKR2 = %08X, QSTR2 = %08X\n", MC_MASKR2, MC_QSTR2); //spw_clean_dma (u); spw_start (u); PDEBUG ("=== spw_open done!\n"); return 0; } static int spw_close (struct inode *inode, struct file *file) { unsigned chan_num = iminor(inode); // получаем номер канала SpW spw_t *u = &spw_channel [chan_num]; PDEBUGG ("=== spw_close\n"); free_irq (spw_rx_desc_irq (u->port), u); free_irq (spw_rx_data_irq (u->port), u); free_irq (spw_tx_data_irq (u->port), u); free_irq (spw_link_irq (u->port), u); spw_stop (u); u->open = 0; return 0; } /* static int detect_spw (void) { const unsigned EXPECTED_HW_VER = 0x00000002; int i; for (i = 0; i < NB_OF_CHANNELS; ++i) if (MC_SWIC_HW_VER(i) != EXPECTED_HW_VER) return 0; return 1; } */ static int __init spw_module_init(void) { int err; PDEBUGG ("=== spw_module_init\n"); // Определение мезонина /* if (! detect_spw ()) { printk (DEVNAME ": not detected!\n"); err = -ENODEV; goto err_out1; } */ if (register_chrdev (SPW_MAJOR, DEVNAME, &spw_fops) < 0) { printk (DEVNAME ": unable to create char device with major number %d\n", SPW_MAJOR); err = -ENODEV; goto err_out1; } spw_class = class_create (THIS_MODULE, "spacewire"); if (IS_ERR (spw_class)) { printk (DEVNAME ": IS_ERR!\n"); err = PTR_ERR (spw_class); goto err_out2; } PDEBUGG ("=== spw_module_init done!\n"); return 0; err_out2: unregister_chrdev (SPW_MAJOR, DEVNAME); err_out1: return err; } static void __exit spw_module_cleanup(void) { } module_init(spw_module_init); module_exit(spw_module_cleanup);