162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * nosy - Snoop mode driver for TI PCILynx 1394 controllers 462306a36Sopenharmony_ci * Copyright (C) 2002-2007 Kristian Høgsberg 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/kref.h> 1562306a36Sopenharmony_ci#include <linux/miscdevice.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/poll.h> 2062306a36Sopenharmony_ci#include <linux/sched.h> /* required for linux/wait.h */ 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/time64.h> 2462306a36Sopenharmony_ci#include <linux/timex.h> 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci#include <linux/wait.h> 2762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2862306a36Sopenharmony_ci#include <linux/atomic.h> 2962306a36Sopenharmony_ci#include <asm/byteorder.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "nosy.h" 3262306a36Sopenharmony_ci#include "nosy-user.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define TCODE_PHY_PACKET 0x10 3562306a36Sopenharmony_ci#define PCI_DEVICE_ID_TI_PCILYNX 0x8000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic char driver_name[] = KBUILD_MODNAME; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* this is the physical layout of a PCL, its size is 128 bytes */ 4062306a36Sopenharmony_cistruct pcl { 4162306a36Sopenharmony_ci __le32 next; 4262306a36Sopenharmony_ci __le32 async_error_next; 4362306a36Sopenharmony_ci u32 user_data; 4462306a36Sopenharmony_ci __le32 pcl_status; 4562306a36Sopenharmony_ci __le32 remaining_transfer_count; 4662306a36Sopenharmony_ci __le32 next_data_buffer; 4762306a36Sopenharmony_ci struct { 4862306a36Sopenharmony_ci __le32 control; 4962306a36Sopenharmony_ci __le32 pointer; 5062306a36Sopenharmony_ci } buffer[13]; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct packet { 5462306a36Sopenharmony_ci unsigned int length; 5562306a36Sopenharmony_ci char data[]; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct packet_buffer { 5962306a36Sopenharmony_ci char *data; 6062306a36Sopenharmony_ci size_t capacity; 6162306a36Sopenharmony_ci long total_packet_count, lost_packet_count; 6262306a36Sopenharmony_ci atomic_t size; 6362306a36Sopenharmony_ci struct packet *head, *tail; 6462306a36Sopenharmony_ci wait_queue_head_t wait; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct pcilynx { 6862306a36Sopenharmony_ci struct pci_dev *pci_device; 6962306a36Sopenharmony_ci __iomem char *registers; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci struct pcl *rcv_start_pcl, *rcv_pcl; 7262306a36Sopenharmony_ci __le32 *rcv_buffer; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci spinlock_t client_list_lock; 7762306a36Sopenharmony_ci struct list_head client_list; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci struct miscdevice misc; 8062306a36Sopenharmony_ci struct list_head link; 8162306a36Sopenharmony_ci struct kref kref; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic inline struct pcilynx * 8562306a36Sopenharmony_cilynx_get(struct pcilynx *lynx) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci kref_get(&lynx->kref); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return lynx; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void 9362306a36Sopenharmony_cilynx_release(struct kref *kref) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci kfree(container_of(kref, struct pcilynx, kref)); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic inline void 9962306a36Sopenharmony_cilynx_put(struct pcilynx *lynx) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci kref_put(&lynx->kref, lynx_release); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct client { 10562306a36Sopenharmony_ci struct pcilynx *lynx; 10662306a36Sopenharmony_ci u32 tcode_mask; 10762306a36Sopenharmony_ci struct packet_buffer buffer; 10862306a36Sopenharmony_ci struct list_head link; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic DEFINE_MUTEX(card_mutex); 11262306a36Sopenharmony_cistatic LIST_HEAD(card_list); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int 11562306a36Sopenharmony_cipacket_buffer_init(struct packet_buffer *buffer, size_t capacity) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci buffer->data = kmalloc(capacity, GFP_KERNEL); 11862306a36Sopenharmony_ci if (buffer->data == NULL) 11962306a36Sopenharmony_ci return -ENOMEM; 12062306a36Sopenharmony_ci buffer->head = (struct packet *) buffer->data; 12162306a36Sopenharmony_ci buffer->tail = (struct packet *) buffer->data; 12262306a36Sopenharmony_ci buffer->capacity = capacity; 12362306a36Sopenharmony_ci buffer->lost_packet_count = 0; 12462306a36Sopenharmony_ci atomic_set(&buffer->size, 0); 12562306a36Sopenharmony_ci init_waitqueue_head(&buffer->wait); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void 13162306a36Sopenharmony_cipacket_buffer_destroy(struct packet_buffer *buffer) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci kfree(buffer->data); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int 13762306a36Sopenharmony_cipacket_buffer_get(struct client *client, char __user *data, size_t user_length) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct packet_buffer *buffer = &client->buffer; 14062306a36Sopenharmony_ci size_t length; 14162306a36Sopenharmony_ci char *end; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (wait_event_interruptible(buffer->wait, 14462306a36Sopenharmony_ci atomic_read(&buffer->size) > 0) || 14562306a36Sopenharmony_ci list_empty(&client->lynx->link)) 14662306a36Sopenharmony_ci return -ERESTARTSYS; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (atomic_read(&buffer->size) == 0) 14962306a36Sopenharmony_ci return -ENODEV; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* FIXME: Check length <= user_length. */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci end = buffer->data + buffer->capacity; 15462306a36Sopenharmony_ci length = buffer->head->length; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (&buffer->head->data[length] < end) { 15762306a36Sopenharmony_ci if (copy_to_user(data, buffer->head->data, length)) 15862306a36Sopenharmony_ci return -EFAULT; 15962306a36Sopenharmony_ci buffer->head = (struct packet *) &buffer->head->data[length]; 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci size_t split = end - buffer->head->data; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (copy_to_user(data, buffer->head->data, split)) 16462306a36Sopenharmony_ci return -EFAULT; 16562306a36Sopenharmony_ci if (copy_to_user(data + split, buffer->data, length - split)) 16662306a36Sopenharmony_ci return -EFAULT; 16762306a36Sopenharmony_ci buffer->head = (struct packet *) &buffer->data[length - split]; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Decrease buffer->size as the last thing, since this is what 17262306a36Sopenharmony_ci * keeps the interrupt from overwriting the packet we are 17362306a36Sopenharmony_ci * retrieving from the buffer. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci atomic_sub(sizeof(struct packet) + length, &buffer->size); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return length; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void 18162306a36Sopenharmony_cipacket_buffer_put(struct packet_buffer *buffer, void *data, size_t length) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci char *end; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci buffer->total_packet_count++; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (buffer->capacity < 18862306a36Sopenharmony_ci atomic_read(&buffer->size) + sizeof(struct packet) + length) { 18962306a36Sopenharmony_ci buffer->lost_packet_count++; 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci end = buffer->data + buffer->capacity; 19462306a36Sopenharmony_ci buffer->tail->length = length; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (&buffer->tail->data[length] < end) { 19762306a36Sopenharmony_ci memcpy(buffer->tail->data, data, length); 19862306a36Sopenharmony_ci buffer->tail = (struct packet *) &buffer->tail->data[length]; 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci size_t split = end - buffer->tail->data; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci memcpy(buffer->tail->data, data, split); 20362306a36Sopenharmony_ci memcpy(buffer->data, data + split, length - split); 20462306a36Sopenharmony_ci buffer->tail = (struct packet *) &buffer->data[length - split]; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Finally, adjust buffer size and wake up userspace reader. */ 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci atomic_add(sizeof(struct packet) + length, &buffer->size); 21062306a36Sopenharmony_ci wake_up_interruptible(&buffer->wait); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic inline void 21462306a36Sopenharmony_cireg_write(struct pcilynx *lynx, int offset, u32 data) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci writel(data, lynx->registers + offset); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic inline u32 22062306a36Sopenharmony_cireg_read(struct pcilynx *lynx, int offset) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci return readl(lynx->registers + offset); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic inline void 22662306a36Sopenharmony_cireg_set_bits(struct pcilynx *lynx, int offset, u32 mask) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * Maybe the pcl programs could be set up to just append data instead 23362306a36Sopenharmony_ci * of using a whole packet. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cistatic inline void 23662306a36Sopenharmony_cirun_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, 23762306a36Sopenharmony_ci int dmachan) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus); 24062306a36Sopenharmony_ci reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, 24162306a36Sopenharmony_ci DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int 24562306a36Sopenharmony_ciset_phy_reg(struct pcilynx *lynx, int addr, int val) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (addr > 15) { 24862306a36Sopenharmony_ci dev_err(&lynx->pci_device->dev, 24962306a36Sopenharmony_ci "PHY register address %d out of range\n", addr); 25062306a36Sopenharmony_ci return -1; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci if (val > 0xff) { 25362306a36Sopenharmony_ci dev_err(&lynx->pci_device->dev, 25462306a36Sopenharmony_ci "PHY register value %d out of range\n", val); 25562306a36Sopenharmony_ci return -1; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | 25862306a36Sopenharmony_ci LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int 26462306a36Sopenharmony_cinosy_open(struct inode *inode, struct file *file) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci int minor = iminor(inode); 26762306a36Sopenharmony_ci struct client *client; 26862306a36Sopenharmony_ci struct pcilynx *tmp, *lynx = NULL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci mutex_lock(&card_mutex); 27162306a36Sopenharmony_ci list_for_each_entry(tmp, &card_list, link) 27262306a36Sopenharmony_ci if (tmp->misc.minor == minor) { 27362306a36Sopenharmony_ci lynx = lynx_get(tmp); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci mutex_unlock(&card_mutex); 27762306a36Sopenharmony_ci if (lynx == NULL) 27862306a36Sopenharmony_ci return -ENODEV; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci client = kmalloc(sizeof *client, GFP_KERNEL); 28162306a36Sopenharmony_ci if (client == NULL) 28262306a36Sopenharmony_ci goto fail; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci client->tcode_mask = ~0; 28562306a36Sopenharmony_ci client->lynx = lynx; 28662306a36Sopenharmony_ci INIT_LIST_HEAD(&client->link); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) 28962306a36Sopenharmony_ci goto fail; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci file->private_data = client; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return stream_open(inode, file); 29462306a36Sopenharmony_cifail: 29562306a36Sopenharmony_ci kfree(client); 29662306a36Sopenharmony_ci lynx_put(lynx); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int 30262306a36Sopenharmony_cinosy_release(struct inode *inode, struct file *file) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct client *client = file->private_data; 30562306a36Sopenharmony_ci struct pcilynx *lynx = client->lynx; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci spin_lock_irq(&lynx->client_list_lock); 30862306a36Sopenharmony_ci list_del_init(&client->link); 30962306a36Sopenharmony_ci spin_unlock_irq(&lynx->client_list_lock); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci packet_buffer_destroy(&client->buffer); 31262306a36Sopenharmony_ci kfree(client); 31362306a36Sopenharmony_ci lynx_put(lynx); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic __poll_t 31962306a36Sopenharmony_cinosy_poll(struct file *file, poll_table *pt) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct client *client = file->private_data; 32262306a36Sopenharmony_ci __poll_t ret = 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci poll_wait(file, &client->buffer.wait, pt); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (atomic_read(&client->buffer.size) > 0) 32762306a36Sopenharmony_ci ret = EPOLLIN | EPOLLRDNORM; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (list_empty(&client->lynx->link)) 33062306a36Sopenharmony_ci ret |= EPOLLHUP; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic ssize_t 33662306a36Sopenharmony_cinosy_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct client *client = file->private_data; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return packet_buffer_get(client, buffer, count); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic long 34462306a36Sopenharmony_cinosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct client *client = file->private_data; 34762306a36Sopenharmony_ci spinlock_t *client_list_lock = &client->lynx->client_list_lock; 34862306a36Sopenharmony_ci struct nosy_stats stats; 34962306a36Sopenharmony_ci int ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (cmd) { 35262306a36Sopenharmony_ci case NOSY_IOC_GET_STATS: 35362306a36Sopenharmony_ci spin_lock_irq(client_list_lock); 35462306a36Sopenharmony_ci stats.total_packet_count = client->buffer.total_packet_count; 35562306a36Sopenharmony_ci stats.lost_packet_count = client->buffer.lost_packet_count; 35662306a36Sopenharmony_ci spin_unlock_irq(client_list_lock); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (copy_to_user((void __user *) arg, &stats, sizeof stats)) 35962306a36Sopenharmony_ci return -EFAULT; 36062306a36Sopenharmony_ci else 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci case NOSY_IOC_START: 36462306a36Sopenharmony_ci ret = -EBUSY; 36562306a36Sopenharmony_ci spin_lock_irq(client_list_lock); 36662306a36Sopenharmony_ci if (list_empty(&client->link)) { 36762306a36Sopenharmony_ci list_add_tail(&client->link, &client->lynx->client_list); 36862306a36Sopenharmony_ci ret = 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci spin_unlock_irq(client_list_lock); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci case NOSY_IOC_STOP: 37562306a36Sopenharmony_ci spin_lock_irq(client_list_lock); 37662306a36Sopenharmony_ci list_del_init(&client->link); 37762306a36Sopenharmony_ci spin_unlock_irq(client_list_lock); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci case NOSY_IOC_FILTER: 38262306a36Sopenharmony_ci spin_lock_irq(client_list_lock); 38362306a36Sopenharmony_ci client->tcode_mask = arg; 38462306a36Sopenharmony_ci spin_unlock_irq(client_list_lock); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci /* Flush buffer, configure filter. */ 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct file_operations nosy_ops = { 39562306a36Sopenharmony_ci .owner = THIS_MODULE, 39662306a36Sopenharmony_ci .read = nosy_read, 39762306a36Sopenharmony_ci .unlocked_ioctl = nosy_ioctl, 39862306a36Sopenharmony_ci .poll = nosy_poll, 39962306a36Sopenharmony_ci .open = nosy_open, 40062306a36Sopenharmony_ci .release = nosy_release, 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void 40662306a36Sopenharmony_cipacket_irq_handler(struct pcilynx *lynx) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct client *client; 40962306a36Sopenharmony_ci u32 tcode_mask, tcode, timestamp; 41062306a36Sopenharmony_ci size_t length; 41162306a36Sopenharmony_ci struct timespec64 ts64; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* FIXME: Also report rcv_speed. */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci length = __le32_to_cpu(lynx->rcv_pcl->pcl_status) & 0x00001fff; 41662306a36Sopenharmony_ci tcode = __le32_to_cpu(lynx->rcv_buffer[1]) >> 4 & 0xf; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ktime_get_real_ts64(&ts64); 41962306a36Sopenharmony_ci timestamp = ts64.tv_nsec / NSEC_PER_USEC; 42062306a36Sopenharmony_ci lynx->rcv_buffer[0] = (__force __le32)timestamp; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (length == PHY_PACKET_SIZE) 42362306a36Sopenharmony_ci tcode_mask = 1 << TCODE_PHY_PACKET; 42462306a36Sopenharmony_ci else 42562306a36Sopenharmony_ci tcode_mask = 1 << tcode; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci spin_lock(&lynx->client_list_lock); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci list_for_each_entry(client, &lynx->client_list, link) 43062306a36Sopenharmony_ci if (client->tcode_mask & tcode_mask) 43162306a36Sopenharmony_ci packet_buffer_put(&client->buffer, 43262306a36Sopenharmony_ci lynx->rcv_buffer, length + 4); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci spin_unlock(&lynx->client_list_lock); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void 43862306a36Sopenharmony_cibus_reset_irq_handler(struct pcilynx *lynx) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct client *client; 44162306a36Sopenharmony_ci struct timespec64 ts64; 44262306a36Sopenharmony_ci u32 timestamp; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ktime_get_real_ts64(&ts64); 44562306a36Sopenharmony_ci timestamp = ts64.tv_nsec / NSEC_PER_USEC; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci spin_lock(&lynx->client_list_lock); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci list_for_each_entry(client, &lynx->client_list, link) 45062306a36Sopenharmony_ci packet_buffer_put(&client->buffer, ×tamp, 4); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci spin_unlock(&lynx->client_list_lock); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic irqreturn_t 45662306a36Sopenharmony_ciirq_handler(int irq, void *device) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct pcilynx *lynx = device; 45962306a36Sopenharmony_ci u32 pci_int_status; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci pci_int_status = reg_read(lynx, PCI_INT_STATUS); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (pci_int_status == ~0) 46462306a36Sopenharmony_ci /* Card was ejected. */ 46562306a36Sopenharmony_ci return IRQ_NONE; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if ((pci_int_status & PCI_INT_INT_PEND) == 0) 46862306a36Sopenharmony_ci /* Not our interrupt, bail out quickly. */ 46962306a36Sopenharmony_ci return IRQ_NONE; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if ((pci_int_status & PCI_INT_P1394_INT) != 0) { 47262306a36Sopenharmony_ci u32 link_int_status; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci link_int_status = reg_read(lynx, LINK_INT_STATUS); 47562306a36Sopenharmony_ci reg_write(lynx, LINK_INT_STATUS, link_int_status); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0) 47862306a36Sopenharmony_ci bus_reset_irq_handler(lynx); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Clear the PCI_INT_STATUS register only after clearing the 48262306a36Sopenharmony_ci * LINK_INT_STATUS register; otherwise the PCI_INT_P1394 will 48362306a36Sopenharmony_ci * be set again immediately. */ 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci reg_write(lynx, PCI_INT_STATUS, pci_int_status); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) { 48862306a36Sopenharmony_ci packet_irq_handler(lynx); 48962306a36Sopenharmony_ci run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return IRQ_HANDLED; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void 49662306a36Sopenharmony_ciremove_card(struct pci_dev *dev) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct pcilynx *lynx = pci_get_drvdata(dev); 49962306a36Sopenharmony_ci struct client *client; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci mutex_lock(&card_mutex); 50262306a36Sopenharmony_ci list_del_init(&lynx->link); 50362306a36Sopenharmony_ci misc_deregister(&lynx->misc); 50462306a36Sopenharmony_ci mutex_unlock(&card_mutex); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci reg_write(lynx, PCI_INT_ENABLE, 0); 50762306a36Sopenharmony_ci free_irq(lynx->pci_device->irq, lynx); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci spin_lock_irq(&lynx->client_list_lock); 51062306a36Sopenharmony_ci list_for_each_entry(client, &lynx->client_list, link) 51162306a36Sopenharmony_ci wake_up_interruptible(&client->buffer.wait); 51262306a36Sopenharmony_ci spin_unlock_irq(&lynx->client_list_lock); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), 51562306a36Sopenharmony_ci lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); 51662306a36Sopenharmony_ci dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), 51762306a36Sopenharmony_ci lynx->rcv_pcl, lynx->rcv_pcl_bus); 51862306a36Sopenharmony_ci dma_free_coherent(&lynx->pci_device->dev, PAGE_SIZE, lynx->rcv_buffer, 51962306a36Sopenharmony_ci lynx->rcv_buffer_bus); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci iounmap(lynx->registers); 52262306a36Sopenharmony_ci pci_disable_device(dev); 52362306a36Sopenharmony_ci lynx_put(lynx); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci#define RCV_BUFFER_SIZE (16 * 1024) 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int 52962306a36Sopenharmony_ciadd_card(struct pci_dev *dev, const struct pci_device_id *unused) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct pcilynx *lynx; 53262306a36Sopenharmony_ci u32 p, end; 53362306a36Sopenharmony_ci int ret, i; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) { 53662306a36Sopenharmony_ci dev_err(&dev->dev, 53762306a36Sopenharmony_ci "DMA address limits not supported for PCILynx hardware\n"); 53862306a36Sopenharmony_ci return -ENXIO; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci if (pci_enable_device(dev)) { 54162306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to enable PCILynx hardware\n"); 54262306a36Sopenharmony_ci return -ENXIO; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci pci_set_master(dev); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci lynx = kzalloc(sizeof *lynx, GFP_KERNEL); 54762306a36Sopenharmony_ci if (lynx == NULL) { 54862306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to allocate control structure\n"); 54962306a36Sopenharmony_ci ret = -ENOMEM; 55062306a36Sopenharmony_ci goto fail_disable; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci lynx->pci_device = dev; 55362306a36Sopenharmony_ci pci_set_drvdata(dev, lynx); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci spin_lock_init(&lynx->client_list_lock); 55662306a36Sopenharmony_ci INIT_LIST_HEAD(&lynx->client_list); 55762306a36Sopenharmony_ci kref_init(&lynx->kref); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci lynx->registers = ioremap(pci_resource_start(dev, 0), 56062306a36Sopenharmony_ci PCILYNX_MAX_REGISTER); 56162306a36Sopenharmony_ci if (lynx->registers == NULL) { 56262306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to map registers\n"); 56362306a36Sopenharmony_ci ret = -ENOMEM; 56462306a36Sopenharmony_ci goto fail_deallocate_lynx; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci lynx->rcv_start_pcl = dma_alloc_coherent(&lynx->pci_device->dev, 56862306a36Sopenharmony_ci sizeof(struct pcl), 56962306a36Sopenharmony_ci &lynx->rcv_start_pcl_bus, 57062306a36Sopenharmony_ci GFP_KERNEL); 57162306a36Sopenharmony_ci lynx->rcv_pcl = dma_alloc_coherent(&lynx->pci_device->dev, 57262306a36Sopenharmony_ci sizeof(struct pcl), 57362306a36Sopenharmony_ci &lynx->rcv_pcl_bus, GFP_KERNEL); 57462306a36Sopenharmony_ci lynx->rcv_buffer = dma_alloc_coherent(&lynx->pci_device->dev, 57562306a36Sopenharmony_ci RCV_BUFFER_SIZE, 57662306a36Sopenharmony_ci &lynx->rcv_buffer_bus, GFP_KERNEL); 57762306a36Sopenharmony_ci if (lynx->rcv_start_pcl == NULL || 57862306a36Sopenharmony_ci lynx->rcv_pcl == NULL || 57962306a36Sopenharmony_ci lynx->rcv_buffer == NULL) { 58062306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to allocate receive buffer\n"); 58162306a36Sopenharmony_ci ret = -ENOMEM; 58262306a36Sopenharmony_ci goto fail_deallocate_buffers; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci lynx->rcv_start_pcl->next = cpu_to_le32(lynx->rcv_pcl_bus); 58562306a36Sopenharmony_ci lynx->rcv_pcl->next = cpu_to_le32(PCL_NEXT_INVALID); 58662306a36Sopenharmony_ci lynx->rcv_pcl->async_error_next = cpu_to_le32(PCL_NEXT_INVALID); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci lynx->rcv_pcl->buffer[0].control = 58962306a36Sopenharmony_ci cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2044); 59062306a36Sopenharmony_ci lynx->rcv_pcl->buffer[0].pointer = 59162306a36Sopenharmony_ci cpu_to_le32(lynx->rcv_buffer_bus + 4); 59262306a36Sopenharmony_ci p = lynx->rcv_buffer_bus + 2048; 59362306a36Sopenharmony_ci end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE; 59462306a36Sopenharmony_ci for (i = 1; p < end; i++, p += 2048) { 59562306a36Sopenharmony_ci lynx->rcv_pcl->buffer[i].control = 59662306a36Sopenharmony_ci cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2048); 59762306a36Sopenharmony_ci lynx->rcv_pcl->buffer[i].pointer = cpu_to_le32(p); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci lynx->rcv_pcl->buffer[i - 1].control |= cpu_to_le32(PCL_LAST_BUFF); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); 60262306a36Sopenharmony_ci /* Fix buggy cards with autoboot pin not tied low: */ 60362306a36Sopenharmony_ci reg_write(lynx, DMA0_CHAN_CTRL, 0); 60462306a36Sopenharmony_ci reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci#if 0 60762306a36Sopenharmony_ci /* now, looking for PHY register set */ 60862306a36Sopenharmony_ci if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { 60962306a36Sopenharmony_ci lynx->phyic.reg_1394a = 1; 61062306a36Sopenharmony_ci PRINT(KERN_INFO, lynx->id, 61162306a36Sopenharmony_ci "found 1394a conform PHY (using extended register set)"); 61262306a36Sopenharmony_ci lynx->phyic.vendor = get_phy_vendorid(lynx); 61362306a36Sopenharmony_ci lynx->phyic.product = get_phy_productid(lynx); 61462306a36Sopenharmony_ci } else { 61562306a36Sopenharmony_ci lynx->phyic.reg_1394a = 0; 61662306a36Sopenharmony_ci PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci#endif 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Setup the general receive FIFO max size. */ 62162306a36Sopenharmony_ci reg_write(lynx, FIFO_SIZES, 255); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci reg_write(lynx, LINK_INT_ENABLE, 62662306a36Sopenharmony_ci LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD | 62762306a36Sopenharmony_ci LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK | 62862306a36Sopenharmony_ci LINK_INT_AT_STUCK | LINK_INT_SNTRJ | 62962306a36Sopenharmony_ci LINK_INT_TC_ERR | LINK_INT_GRF_OVER_FLOW | 63062306a36Sopenharmony_ci LINK_INT_ITF_UNDER_FLOW | LINK_INT_ATF_UNDER_FLOW); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Disable the L flag in self ID packets. */ 63362306a36Sopenharmony_ci set_phy_reg(lynx, 4, 0); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Put this baby into snoop mode */ 63662306a36Sopenharmony_ci reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_SNOOP_ENABLE); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (request_irq(dev->irq, irq_handler, IRQF_SHARED, 64162306a36Sopenharmony_ci driver_name, lynx)) { 64262306a36Sopenharmony_ci dev_err(&dev->dev, 64362306a36Sopenharmony_ci "Failed to allocate shared interrupt %d\n", dev->irq); 64462306a36Sopenharmony_ci ret = -EIO; 64562306a36Sopenharmony_ci goto fail_deallocate_buffers; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci lynx->misc.parent = &dev->dev; 64962306a36Sopenharmony_ci lynx->misc.minor = MISC_DYNAMIC_MINOR; 65062306a36Sopenharmony_ci lynx->misc.name = "nosy"; 65162306a36Sopenharmony_ci lynx->misc.fops = &nosy_ops; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci mutex_lock(&card_mutex); 65462306a36Sopenharmony_ci ret = misc_register(&lynx->misc); 65562306a36Sopenharmony_ci if (ret) { 65662306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to register misc char device\n"); 65762306a36Sopenharmony_ci mutex_unlock(&card_mutex); 65862306a36Sopenharmony_ci goto fail_free_irq; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci list_add_tail(&lynx->link, &card_list); 66162306a36Sopenharmony_ci mutex_unlock(&card_mutex); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci dev_info(&dev->dev, 66462306a36Sopenharmony_ci "Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return 0; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cifail_free_irq: 66962306a36Sopenharmony_ci reg_write(lynx, PCI_INT_ENABLE, 0); 67062306a36Sopenharmony_ci free_irq(lynx->pci_device->irq, lynx); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cifail_deallocate_buffers: 67362306a36Sopenharmony_ci if (lynx->rcv_start_pcl) 67462306a36Sopenharmony_ci dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), 67562306a36Sopenharmony_ci lynx->rcv_start_pcl, 67662306a36Sopenharmony_ci lynx->rcv_start_pcl_bus); 67762306a36Sopenharmony_ci if (lynx->rcv_pcl) 67862306a36Sopenharmony_ci dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), 67962306a36Sopenharmony_ci lynx->rcv_pcl, lynx->rcv_pcl_bus); 68062306a36Sopenharmony_ci if (lynx->rcv_buffer) 68162306a36Sopenharmony_ci dma_free_coherent(&lynx->pci_device->dev, PAGE_SIZE, 68262306a36Sopenharmony_ci lynx->rcv_buffer, lynx->rcv_buffer_bus); 68362306a36Sopenharmony_ci iounmap(lynx->registers); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cifail_deallocate_lynx: 68662306a36Sopenharmony_ci kfree(lynx); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cifail_disable: 68962306a36Sopenharmony_ci pci_disable_device(dev); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return ret; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic struct pci_device_id pci_table[] = { 69562306a36Sopenharmony_ci { 69662306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_TI, 69762306a36Sopenharmony_ci .device = PCI_DEVICE_ID_TI_PCILYNX, 69862306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 69962306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 70062306a36Sopenharmony_ci }, 70162306a36Sopenharmony_ci { } /* Terminating entry */ 70262306a36Sopenharmony_ci}; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_table); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic struct pci_driver lynx_pci_driver = { 70762306a36Sopenharmony_ci .name = driver_name, 70862306a36Sopenharmony_ci .id_table = pci_table, 70962306a36Sopenharmony_ci .probe = add_card, 71062306a36Sopenharmony_ci .remove = remove_card, 71162306a36Sopenharmony_ci}; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cimodule_pci_driver(lynx_pci_driver); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ciMODULE_AUTHOR("Kristian Hoegsberg"); 71662306a36Sopenharmony_ciMODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers"); 71762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 718