diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/dma-coherent.c | 25 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 2 | ||||
-rw-r--r-- | drivers/firewire/core-cdev.c | 4 | ||||
-rw-r--r-- | drivers/firmware/efi/efivars.c | 2 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/amdkfd/kfd_process.c | 2 | ||||
-rw-r--r-- | drivers/hid/uhid.c | 2 | ||||
-rw-r--r-- | drivers/input/input-compat.h | 12 | ||||
-rw-r--r-- | drivers/net/rionet.c | 277 | ||||
-rw-r--r-- | drivers/rapidio/Kconfig | 8 | ||||
-rw-r--r-- | drivers/rapidio/devices/Makefile | 1 | ||||
-rw-r--r-- | drivers/rapidio/devices/rio_mport_cdev.c | 2720 | ||||
-rw-r--r-- | drivers/rapidio/devices/tsi721.c | 1034 | ||||
-rw-r--r-- | drivers/rapidio/devices/tsi721.h | 87 | ||||
-rw-r--r-- | drivers/rapidio/devices/tsi721_dma.c | 397 | ||||
-rw-r--r-- | drivers/rapidio/rio-driver.c | 12 | ||||
-rw-r--r-- | drivers/rapidio/rio-scan.c | 135 | ||||
-rw-r--r-- | drivers/rapidio/rio.c | 433 | ||||
-rw-r--r-- | drivers/rapidio/rio.h | 5 | ||||
-rw-r--r-- | drivers/staging/lustre/lustre/llite/llite_internal.h | 2 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 11 |
22 files changed, 4534 insertions, 642 deletions
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 87b808374888..bdf28f7dd5e8 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -2,6 +2,7 @@ * Coherent per-device memory handling. * Borrowed from i386 */ +#include <linux/io.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> @@ -31,7 +32,10 @@ static bool dma_init_coherent_memory( if (!size) goto out; - mem_base = ioremap(phys_addr, size); + if (flags & DMA_MEMORY_MAP) + mem_base = memremap(phys_addr, size, MEMREMAP_WC); + else + mem_base = ioremap(phys_addr, size); if (!mem_base) goto out; @@ -54,8 +58,12 @@ static bool dma_init_coherent_memory( out: kfree(dma_mem); - if (mem_base) - iounmap(mem_base); + if (mem_base) { + if (flags & DMA_MEMORY_MAP) + memunmap(mem_base); + else + iounmap(mem_base); + } return false; } @@ -63,7 +71,11 @@ static void dma_release_coherent_memory(struct dma_coherent_mem *mem) { if (!mem) return; - iounmap(mem->virt_base); + + if (mem->flags & DMA_MEMORY_MAP) + memunmap(mem->virt_base); + else + iounmap(mem->virt_base); kfree(mem->bitmap); kfree(mem); } @@ -175,7 +187,10 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, */ *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); *ret = mem->virt_base + (pageno << PAGE_SHIFT); - memset(*ret, 0, size); + if (mem->flags & DMA_MEMORY_MAP) + memset(*ret, 0, size); + else + memset_io(*ret, 0, size); spin_unlock_irqrestore(&mem->spinlock, flags); return 1; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 096f0cef4da1..4facc7517a6a 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1140,7 +1140,7 @@ ipmi_nmi(unsigned int val, struct pt_regs *regs) the timer. So do so. */ pretimeout_since_last_heartbeat = 1; if (atomic_inc_and_test(&preop_panic_excl)) - panic(PFX "pre-timeout"); + nmi_panic(regs, PFX "pre-timeout"); } return NMI_HANDLED; diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 36a7c2d89a01..aee149bdf4c0 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -221,7 +221,7 @@ struct inbound_phy_packet_event { #ifdef CONFIG_COMPAT static void __user *u64_to_uptr(u64 value) { - if (is_compat_task()) + if (in_compat_syscall()) return compat_ptr(value); else return (void __user *)(unsigned long)value; @@ -229,7 +229,7 @@ static void __user *u64_to_uptr(u64 value) static u64 uptr_to_u64(void __user *ptr) { - if (is_compat_task()) + if (in_compat_syscall()) return ptr_to_compat(ptr); else return (u64)(unsigned long)ptr; diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index b23a271c6ae5..096adcbcb5a9 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -231,7 +231,7 @@ sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor, static inline bool is_compat(void) { - if (IS_ENABLED(CONFIG_COMPAT) && is_compat_task()) + if (IS_ENABLED(CONFIG_COMPAT) && in_compat_syscall()) return true; return false; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index a15841eced4e..da99bbb74aeb 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -25,6 +25,9 @@ KASAN_SANITIZE := n UBSAN_SANITIZE := n OBJECT_FILES_NON_STANDARD := y +# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in. +KCOV_INSTRUMENT := n + lib-y := efi-stub-helper.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index d2b49c026cf6..07ac724e3ec9 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -107,7 +107,7 @@ static int kfd_open(struct inode *inode, struct file *filep) if (iminor(inode) != 0) return -ENODEV; - is_32bit_user_mode = is_compat_task(); + is_32bit_user_mode = in_compat_syscall(); if (is_32bit_user_mode == true) { dev_warn(kfd_device, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index a902ae037398..ac005796b71c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -311,7 +311,7 @@ static struct kfd_process *create_process(const struct task_struct *thread) goto err_process_pqm_init; /* init process apertures*/ - process->is_32bit_user_mode = is_compat_task(); + process->is_32bit_user_mode = in_compat_syscall(); if (kfd_init_apertures(process) != 0) goto err_init_apretures; diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index e094c572b86e..16b6f11a0700 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -384,7 +384,7 @@ struct uhid_create_req_compat { static int uhid_event_from_user(const char __user *buffer, size_t len, struct uhid_event *event) { - if (is_compat_task()) { + if (in_compat_syscall()) { u32 type; if (get_user(type, buffer)) diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h index 148f66fe3205..0f25878d5fa2 100644 --- a/drivers/input/input-compat.h +++ b/drivers/input/input-compat.h @@ -17,17 +17,7 @@ #ifdef CONFIG_COMPAT -/* Note to the author of this code: did it ever occur to - you why the ifdefs are needed? Think about it again. -AK */ -#if defined(CONFIG_X86_64) || defined(CONFIG_TILE) -# define INPUT_COMPAT_TEST is_compat_task() -#elif defined(CONFIG_S390) -# define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT) -#elif defined(CONFIG_MIPS) -# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR) -#else -# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT) -#endif +#define INPUT_COMPAT_TEST in_compat_syscall() struct input_event_compat { struct compat_timeval time; diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index 01f08a7751f7..9cfe6aeac84e 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -24,6 +24,7 @@ #include <linux/skbuff.h> #include <linux/crc32.h> #include <linux/ethtool.h> +#include <linux/reboot.h> #define DRV_NAME "rionet" #define DRV_VERSION "0.3" @@ -48,6 +49,8 @@ MODULE_LICENSE("GPL"); #define RIONET_TX_RING_SIZE CONFIG_RIONET_TX_SIZE #define RIONET_RX_RING_SIZE CONFIG_RIONET_RX_SIZE #define RIONET_MAX_NETS 8 +#define RIONET_MSG_SIZE RIO_MAX_MSG_SIZE +#define RIONET_MAX_MTU (RIONET_MSG_SIZE - ETH_HLEN) struct rionet_private { struct rio_mport *mport; @@ -60,6 +63,7 @@ struct rionet_private { spinlock_t lock; spinlock_t tx_lock; u32 msg_enable; + bool open; }; struct rionet_peer { @@ -71,6 +75,7 @@ struct rionet_peer { struct rionet_net { struct net_device *ndev; struct list_head peers; + spinlock_t lock; /* net info access lock */ struct rio_dev **active; int nact; /* number of active peers */ }; @@ -232,26 +237,32 @@ static void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u struct net_device *ndev = dev_id; struct rionet_private *rnet = netdev_priv(ndev); struct rionet_peer *peer; + unsigned char netid = rnet->mport->id; if (netif_msg_intr(rnet)) printk(KERN_INFO "%s: doorbell sid %4.4x tid %4.4x info %4.4x", DRV_NAME, sid, tid, info); if (info == RIONET_DOORBELL_JOIN) { - if (!nets[rnet->mport->id].active[sid]) { - list_for_each_entry(peer, - &nets[rnet->mport->id].peers, node) { + if (!nets[netid].active[sid]) { + spin_lock(&nets[netid].lock); + list_for_each_entry(peer, &nets[netid].peers, node) { if (peer->rdev->destid == sid) { - nets[rnet->mport->id].active[sid] = - peer->rdev; - nets[rnet->mport->id].nact++; + nets[netid].active[sid] = peer->rdev; + nets[netid].nact++; } } + spin_unlock(&nets[netid].lock); + rio_mport_send_doorbell(mport, sid, RIONET_DOORBELL_JOIN); } } else if (info == RIONET_DOORBELL_LEAVE) { - nets[rnet->mport->id].active[sid] = NULL; - nets[rnet->mport->id].nact--; + spin_lock(&nets[netid].lock); + if (nets[netid].active[sid]) { + nets[netid].active[sid] = NULL; + nets[netid].nact--; + } + spin_unlock(&nets[netid].lock); } else { if (netif_msg_intr(rnet)) printk(KERN_WARNING "%s: unhandled doorbell\n", @@ -280,7 +291,7 @@ static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbo struct net_device *ndev = dev_id; struct rionet_private *rnet = netdev_priv(ndev); - spin_lock(&rnet->lock); + spin_lock(&rnet->tx_lock); if (netif_msg_intr(rnet)) printk(KERN_INFO @@ -299,14 +310,16 @@ static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbo if (rnet->tx_cnt < RIONET_TX_RING_SIZE) netif_wake_queue(ndev); - spin_unlock(&rnet->lock); + spin_unlock(&rnet->tx_lock); } static int rionet_open(struct net_device *ndev) { int i, rc = 0; - struct rionet_peer *peer, *tmp; + struct rionet_peer *peer; struct rionet_private *rnet = netdev_priv(ndev); + unsigned char netid = rnet->mport->id; + unsigned long flags; if (netif_msg_ifup(rnet)) printk(KERN_INFO "%s: open\n", DRV_NAME); @@ -345,20 +358,13 @@ static int rionet_open(struct net_device *ndev) netif_carrier_on(ndev); netif_start_queue(ndev); - list_for_each_entry_safe(peer, tmp, - &nets[rnet->mport->id].peers, node) { - if (!(peer->res = rio_request_outb_dbell(peer->rdev, - RIONET_DOORBELL_JOIN, - RIONET_DOORBELL_LEAVE))) - { - printk(KERN_ERR "%s: error requesting doorbells\n", - DRV_NAME); - continue; - } - + spin_lock_irqsave(&nets[netid].lock, flags); + list_for_each_entry(peer, &nets[netid].peers, node) { /* Send a join message */ rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN); } + spin_unlock_irqrestore(&nets[netid].lock, flags); + rnet->open = true; out: return rc; @@ -367,7 +373,9 @@ static int rionet_open(struct net_device *ndev) static int rionet_close(struct net_device *ndev) { struct rionet_private *rnet = netdev_priv(ndev); - struct rionet_peer *peer, *tmp; + struct rionet_peer *peer; + unsigned char netid = rnet->mport->id; + unsigned long flags; int i; if (netif_msg_ifup(rnet)) @@ -375,18 +383,21 @@ static int rionet_close(struct net_device *ndev) netif_stop_queue(ndev); netif_carrier_off(ndev); + rnet->open = false; for (i = 0; i < RIONET_RX_RING_SIZE; i++) kfree_skb(rnet->rx_skb[i]); - list_for_each_entry_safe(peer, tmp, - &nets[rnet->mport->id].peers, node) { - if (nets[rnet->mport->id].active[peer->rdev->destid]) { + spin_lock_irqsave(&nets[netid].lock, flags); + list_for_each_entry(peer, &nets[netid].peers, node) { + if (nets[netid].active[peer->rdev->destid]) { rio_send_doorbell(peer->rdev, RIONET_DOORBELL_LEAVE); - nets[rnet->mport->id].active[peer->rdev->destid] = NULL; + nets[netid].active[peer->rdev->destid] = NULL; } - rio_release_outb_dbell(peer->rdev, peer->res); + if (peer->res) + rio_release_outb_dbell(peer->rdev, peer->res); } + spin_unlock_irqrestore(&nets[netid].lock, flags); rio_release_inb_dbell(rnet->mport, RIONET_DOORBELL_JOIN, RIONET_DOORBELL_LEAVE); @@ -400,22 +411,38 @@ static void rionet_remove_dev(struct device *dev, struct subsys_interface *sif) { struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; - struct rionet_peer *peer, *tmp; + struct rionet_peer *peer; + int state, found = 0; + unsigned long flags; - if (dev_rionet_capable(rdev)) { - list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) { - if (peer->rdev == rdev) { - if (nets[netid].active[rdev->destid]) { - nets[netid].active[rdev->destid] = NULL; - nets[netid].nact--; + if (!dev_rionet_capable(rdev)) + return; + + spin_lock_irqsave(&nets[netid].lock, flags); + list_for_each_entry(peer, &nets[netid].peers, node) { + if (peer->rdev == rdev) { + list_del(&peer->node); + if (nets[netid].active[rdev->destid]) { + state = atomic_read(&rdev->state); + if (state != RIO_DEVICE_GONE && + state != RIO_DEVICE_INITIALIZING) { + rio_send_doorbell(rdev, + RIONET_DOORBELL_LEAVE); } - - list_del(&peer->node); - kfree(peer); - break; + nets[netid].active[rdev->destid] = NULL; + nets[netid].nact--; } + found = 1; + break; } } + spin_unlock_irqrestore(&nets[netid].lock, flags); + + if (found) { + if (peer->res) + rio_release_outb_dbell(rdev, peer->res); + kfree(peer); + } } static void rionet_get_drvinfo(struct net_device *ndev, @@ -443,6 +470,17 @@ static void rionet_set_msglevel(struct net_device *ndev, u32 value) rnet->msg_enable = value; } +static int rionet_change_mtu(struct net_device *ndev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > RIONET_MAX_MTU)) { + printk(KERN_ERR "%s: Invalid MTU size %d\n", + ndev->name, new_mtu); + return -EINVAL; + } + ndev->mtu = new_mtu; + return 0; +} + static const struct ethtool_ops rionet_ethtool_ops = { .get_drvinfo = rionet_get_drvinfo, .get_msglevel = rionet_get_msglevel, @@ -454,7 +492,7 @@ static const struct net_device_ops rionet_netdev_ops = { .ndo_open = rionet_open, .ndo_stop = rionet_close, .ndo_start_xmit = rionet_start_xmit, - .ndo_change_mtu = eth_change_mtu, + .ndo_change_mtu = rionet_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; @@ -478,6 +516,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) /* Set up private area */ rnet = netdev_priv(ndev); rnet->mport = mport; + rnet->open = false; /* Set the default MAC address */ device_id = rio_local_get_device_id(mport); @@ -489,7 +528,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) ndev->dev_addr[5] = device_id & 0xff; ndev->netdev_ops = &rionet_netdev_ops; - ndev->mtu = RIO_MAX_MSG_SIZE - 14; + ndev->mtu = RIONET_MAX_MTU; ndev->features = NETIF_F_LLTX; SET_NETDEV_DEV(ndev, &mport->dev); ndev->ethtool_ops = &rionet_ethtool_ops; @@ -500,8 +539,11 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) rnet->msg_enable = RIONET_DEFAULT_MSGLEVEL; rc = register_netdev(ndev); - if (rc != 0) + if (rc != 0) { + free_pages((unsigned long)nets[mport->id].active, + get_order(rionet_active_bytes)); goto out; + } printk(KERN_INFO "%s: %s %s Version %s, MAC %pM, %s\n", ndev->name, @@ -515,8 +557,6 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) return rc; } -static unsigned long net_table[RIONET_MAX_NETS/sizeof(unsigned long) + 1]; - static int rionet_add_dev(struct device *dev, struct subsys_interface *sif) { int rc = -ENODEV; @@ -525,19 +565,16 @@ static int rionet_add_dev(struct device *dev, struct subsys_interface *sif) struct net_device *ndev = NULL; struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; - int oldnet; if (netid >= RIONET_MAX_NETS) return rc; - oldnet = test_and_set_bit(netid, net_table); - /* * If first time through this net, make sure local device is rionet * capable and setup netdev (this step will be skipped in later probes * on the same net). */ - if (!oldnet) { + if (!nets[netid].ndev) { rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR, &lsrc_ops); rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR, @@ -555,30 +592,56 @@ static int rionet_add_dev(struct device *dev, struct subsys_interface *sif) rc = -ENOMEM; goto out; } - nets[netid].ndev = ndev; + rc = rionet_setup_netdev(rdev->net->hport, ndev); if (rc) { printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n", DRV_NAME, rc); + free_netdev(ndev); goto out; } INIT_LIST_HEAD(&nets[netid].peers); + spin_lock_init(&nets[netid].lock); nets[netid].nact = 0; - } else if (nets[netid].ndev == NULL) - goto out; + nets[netid].ndev = ndev; + } /* * If the remote device has mailbox/doorbell capabilities, * add it to the peer list. */ if (dev_rionet_capable(rdev)) { - if (!(peer = kmalloc(sizeof(struct rionet_peer), GFP_KERNEL))) { + struct rionet_private *rnet; + unsigned long flags; + + rnet = netdev_priv(nets[netid].ndev); + + peer = kzalloc(sizeof(*peer), GFP_KERNEL); + if (!peer) { rc = -ENOMEM; goto out; } peer->rdev = rdev; + peer->res = rio_request_outb_dbell(peer->rdev, + RIONET_DOORBELL_JOIN, + RIONET_DOORBELL_LEAVE); + if (!peer->res) { + pr_err("%s: error requesting doorbells\n", DRV_NAME); + kfree(peer); + rc = -ENOMEM; + goto out; + } + + spin_lock_irqsave(&nets[netid].lock, flags); list_add_tail(&peer->node, &nets[netid].peers); + spin_unlock_irqrestore(&nets[netid].lock, flags); + pr_debug("%s: %s add peer %s\n", + DRV_NAME, __func__, rio_name(rdev)); + + /* If netdev is already opened, send join request to new peer */ + if (rnet->open) + rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN); } return 0; @@ -586,6 +649,61 @@ out: return rc; } +static int rionet_shutdown(struct notifier_block *nb, unsigned long code, + void *unused) +{ + struct rionet_peer *peer; + unsigned long flags; + int i; + + pr_debug("%s: %s\n", DRV_NAME, __func__); + + for (i = 0; i < RIONET_MAX_NETS; i++) { + if (!nets[i].ndev) + continue; + + spin_lock_irqsave(&nets[i].lock, flags); + list_for_each_entry(peer, &nets[i].peers, node) { + if (nets[i].active[peer->rdev->destid]) { + rio_send_doorbell(peer->rdev, + RIONET_DOORBELL_LEAVE); + nets[i].active[peer->rdev->destid] = NULL; + } + } + spin_unlock_irqrestore(&nets[i].lock, flags); + } + + return NOTIFY_DONE; +} + +static void rionet_remove_mport(struct device *dev, + struct class_interface *class_intf) +{ + struct rio_mport *mport = to_rio_mport(dev); + struct net_device *ndev; + int id = mport->id; + + pr_debug("%s %s\n", __func__, mport->name); + + WARN(nets[id].nact, "%s called when connected to %d peers\n", + __func__, nets[id].nact); + WARN(!nets[id].ndev, "%s called for mport without NDEV\n", + __func__); + + if (nets[id].ndev) { + ndev = nets[id].ndev; + netif_stop_queue(ndev); + unregister_netdev(ndev); + + free_pages((unsigned long)nets[id].active, + get_order(sizeof(void *) * + RIO_MAX_ROUTE_ENTRIES(mport->sys_size))); + nets[id].active = NULL; + free_netdev(ndev); + nets[id].ndev = NULL; + } +} + #ifdef MODULE static struct rio_device_id rionet_id_table[] = { {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)}, @@ -602,40 +720,43 @@ static struct subsys_interface rionet_interface = { .remove_dev = rionet_remove_dev, }; +static struct notifier_block rionet_notifier = { + .notifier_call = rionet_shutdown, +}; + +/* the rio_mport_interface is used to handle local mport devices */ +static struct class_interface rio_mport_interface __refdata = { + .class = &rio_mport_class, + .add_dev = NULL, + .remove_dev = rionet_remove_mport, +}; + static int __init rionet_init(void) { + int ret; + + ret = register_reboot_notifier(&rionet_notifier); + if (ret) { + pr_err("%s: failed to register reboot notifier (err=%d)\n", + DRV_NAME, ret); + return ret; + } + + ret = class_interface_register(&rio_mport_interface); + if (ret) { + pr_err("%s: class_interface_register error: %d\n", + DRV_NAME, ret); + return ret; + } + return subsys_interface_register(&rionet_interface); } static void __exit rionet_exit(void) { - struct rionet_private *rnet; - struct net_device *ndev; - struct rionet_peer *peer, *tmp; - int i; - - for (i = 0; i < RIONET_MAX_NETS; i++) { - if (nets[i].ndev != NULL) { - ndev = nets[i].ndev; - rnet = netdev_priv(ndev); - unregister_netdev(ndev); - - list_for_each_entry_safe(peer, - tmp, &nets[i].peers, node) { - list_del(&peer->node); - kfree(peer); - } - - free_pages((unsigned long)nets[i].active, - get_order(sizeof(void *) * - RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size))); - nets[i].active = NULL; - - free_netdev(ndev); - } - } - + unregister_reboot_notifier(&rionet_notifier); subsys_interface_unregister(&rionet_interface); + class_interface_unregister(&rio_mport_interface); } late_initcall(rionet_init); diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig index 3e3be57e9a1a..b5a10d3c92c7 100644 --- a/drivers/rapidio/Kconfig +++ b/drivers/rapidio/Kconfig @@ -67,6 +67,14 @@ config RAPIDIO_ENUM_BASIC endchoice +config RAPIDIO_MPORT_CDEV + tristate "RapidIO /dev mport device driver" + depends on RAPIDIO + help + This option includes generic RapidIO mport device driver which + allows to user space applications to perform RapidIO-specific + operations through selected RapidIO mport. + menu "RapidIO Switch drivers" depends on RAPIDIO diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile index 9432c494cf57..927dbf89592b 100644 --- a/drivers/rapidio/devices/Makefile +++ b/drivers/rapidio/devices/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_RAPIDIO_TSI721) += tsi721_mport.o tsi721_mport-y := tsi721.o tsi721_mport-$(CONFIG_RAPIDIO_DMA_ENGINE) += tsi721_dma.o +obj-$(CONFIG_RAPIDIO_MPORT_CDEV) += rio_mport_cdev.o diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c new file mode 100644 index 000000000000..9607bc826460 --- /dev/null +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -0,0 +1,2720 @@ +/* + * RapidIO mport character device + * + * Copyright 2014-2015 Integrated Device Technology, Inc. + * Alexandre Bounine <alexandre.bounine@idt.com> + * Copyright 2014-2015 Prodrive Technologies + * Andre van Herk <andre.van.herk@prodrive-technologies.com> + * Jerry Jacobs <jerry.jacobs@prodrive-technologies.com> + * Copyright (C) 2014 Texas Instruments Incorporated + * Aurelien Jacquiot <a-jacquiot@ti.com> + * + * 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. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/cdev.h> +#include <linux/ioctl.h> +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/net.h> +#include <linux/poll.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/kfifo.h> + +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mman.h> + +#include <linux/dma-mapping.h> +#ifdef CONFIG_RAPIDIO_DMA_ENGINE +#include <linux/dmaengine.h> +#endif + +#include <linux/rio.h> +#include <linux/rio_ids.h> +#include <linux/rio_drv.h> +#include <linux/rio_mport_cdev.h> + +#include "../rio.h" + +#define DRV_NAME "rio_mport" +#define DRV_PREFIX DRV_NAME ": " +#define DEV_NAME "rio_mport" +#define DRV_VERSION "1.0.0" + +/* Debug output filtering masks */ +enum { + DBG_NONE = 0, + DBG_INIT = BIT(0), /* driver init */ + DBG_EXIT = BIT(1), /* driver exit */ + DBG_MPORT = BIT(2), /* mport add/remove */ + DBG_RDEV = BIT(3), /* RapidIO device add/remove */ + DBG_DMA = BIT(4), /* DMA transfer messages */ + DBG_MMAP = BIT(5), /* mapping messages */ + DBG_IBW = BIT(6), /* inbound window */ + DBG_EVENT = BIT(7), /* event handling messages */ + DBG_OBW = BIT(8), /* outbound window messages */ + DBG_DBELL = BIT(9), /* doorbell messages */ + DBG_ALL = ~0, +}; + +#ifdef DEBUG +#define rmcd_debug(level, fmt, arg...) \ + do { \ + if (DBG_##level & dbg_level) \ + pr_debug(DRV_PREFIX "%s: " fmt "\n", __func__, ##arg); \ + } while (0) +#else +#define rmcd_debug(level, fmt, arg...) \ + no_printk(KERN_DEBUG pr_fmt(DRV_PREFIX fmt "\n"), ##arg) +#endif + +#define rmcd_warn(fmt, arg...) \ + pr_warn(DRV_PREFIX "%s WARNING " fmt "\n", __func__, ##arg) + +#define rmcd_error(fmt, arg...) \ + pr_err(DRV_PREFIX "%s ERROR " fmt "\n", __func__, ##arg) + +MODULE_AUTHOR("Jerry Jacobs <jerry.jacobs@prodrive-technologies.com>"); +MODULE_AUTHOR("Aurelien Jacquiot <a-jacquiot@ti.com>"); +MODULE_AUTHOR("Alexandre Bounine <alexandre.bounine@idt.com>"); +MODULE_AUTHOR("Andre van Herk <andre.van.herk@prodrive-technologies.com>"); +MODULE_DESCRIPTION("RapidIO mport character device driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +static int dma_timeout = 3000; /* DMA transfer timeout in msec */ +module_param(dma_timeout, int, S_IRUGO); +MODULE_PARM_DESC(dma_timeout, "DMA Transfer Timeout in msec (default: 3000)"); + +#ifdef DEBUG +static u32 dbg_level = DBG_NONE; +module_param(dbg_level, uint, S_IWUSR | S_IWGRP | S_IRUGO); +MODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)"); +#endif + +/* + * An internal DMA coherent buffer + */ +struct mport_dma_buf { + void *ib_base; + dma_addr_t ib_phys; + u32 ib_size; + u64 ib_rio_base; + bool ib_map; + struct file *filp; +}; + +/* + * Internal memory mapping structure + */ +enum rio_mport_map_dir { + MAP_INBOUND, + MAP_OUTBOUND, + MAP_DMA, +}; + +struct rio_mport_mapping { + struct list_head node; + struct mport_dev *md; + enum rio_mport_map_dir dir; + u32 rioid; + u64 rio_addr; + dma_addr_t phys_addr; /* for mmap */ + void *virt_addr; /* kernel address, for dma_free_coherent */ + u64 size; + struct kref ref; /* refcount of vmas sharing the mapping */ + struct file *filp; +}; + +struct rio_mport_dma_map { + int valid; + uint64_t length; + void *vaddr; + dma_addr_t paddr; +}; + +#define MPORT_MAX_DMA_BUFS 16 +#define MPORT_EVENT_DEPTH 10 + +/* + * mport_dev driver-specific structure that represents mport device + * @active mport device status flag + * @node list node to maintain list of registered mports + * @cdev character device + * @dev associated device object + * @mport associated subsystem's master port device object + * @buf_mutex lock for buffer handling + * @file_mutex - lock for open files list + * @file_list - list of open files on given mport + * @properties properties of this mport + * @portwrites queue of inbound portwrites + * @pw_lock lock for port write queue + * @mappings queue for memory mappings + * @dma_chan DMA channels associated with this device + * @dma_ref: + * @comp: + */ +struct mport_dev { + atomic_t active; + struct list_head node; + struct cdev cdev; + struct device dev; + struct rio_mport *mport; + struct mutex buf_mutex; + struct mutex file_mutex; + struct list_head file_list; + struct rio_mport_properties properties; + struct list_head doorbells; + spinlock_t db_lock; + struct list_head portwrites; + spinlock_t pw_lock; + struct list_head mappings; +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + struct dma_chan *dma_chan; + struct kref dma_ref; + struct completion comp; +#endif +}; + +/* + * mport_cdev_priv - data structure specific to individual file object + * associated with an open device + * @md master port character device object + * @async_queue - asynchronous notification queue + * @list - file objects tracking list + * @db_filters inbound doorbell filters for this descriptor + * @pw_filters portwrite filters for this descriptor + * @event_fifo event fifo for this descriptor + * @event_rx_wait wait queue for this descriptor + * @fifo_lock lock for event_fifo + * @event_mask event mask for this descriptor + * @dmach DMA engine channel allocated for specific file object + */ +struct mport_cdev_priv { + struct mport_dev *md; + struct fasync_struct *async_queue; + struct list_head list; + struct list_head db_filters; + struct list_head pw_filters; + struct kfifo event_fifo; + wait_queue_head_t event_rx_wait; + spinlock_t fifo_lock; + unsigned int event_mask; /* RIO_DOORBELL, RIO_PORTWRITE */ +#ifdef CONFIG_RAPIDIO_DMA_ENGINE + struct dma_chan *dmach; + struct list_head async_list; + struct list_head pend_list; + spinlock_t req_lock; + struct mutex dma_lock; + struct kref dma_ref; + struct completion comp; +#endif +}; + +/* + * rio_mport_pw_f |