18cfab3cfSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0 2080b47deSLogan Gunthorpe /* 3080b47deSLogan Gunthorpe * Microsemi Switchtec(tm) PCIe Management Driver 4080b47deSLogan Gunthorpe * Copyright (c) 2017, Microsemi Corporation 5080b47deSLogan Gunthorpe */ 6080b47deSLogan Gunthorpe 75a1c269fSLogan Gunthorpe #include <linux/switchtec.h> 852eabba5SLogan Gunthorpe #include <linux/switchtec_ioctl.h> 952eabba5SLogan Gunthorpe 10080b47deSLogan Gunthorpe #include <linux/interrupt.h> 11080b47deSLogan Gunthorpe #include <linux/module.h> 12080b47deSLogan Gunthorpe #include <linux/fs.h> 13080b47deSLogan Gunthorpe #include <linux/uaccess.h> 14080b47deSLogan Gunthorpe #include <linux/poll.h> 15080b47deSLogan Gunthorpe #include <linux/wait.h> 16f7eb7b8aSWesley Sheng #include <linux/io-64-nonatomic-lo-hi.h> 1746feb6b4SGustavo A. R. Silva #include <linux/nospec.h> 1846feb6b4SGustavo A. R. Silva 19080b47deSLogan Gunthorpe MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); 20080b47deSLogan Gunthorpe MODULE_VERSION("0.1"); 21080b47deSLogan Gunthorpe MODULE_LICENSE("GPL"); 22080b47deSLogan Gunthorpe MODULE_AUTHOR("Microsemi Corporation"); 23080b47deSLogan Gunthorpe 24080b47deSLogan Gunthorpe static int max_devices = 16; 25080b47deSLogan Gunthorpe module_param(max_devices, int, 0644); 26080b47deSLogan Gunthorpe MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); 27080b47deSLogan Gunthorpe 28b8af8549SKrzysztof Wilczynski static bool use_dma_mrpc = true; 29f7eb7b8aSWesley Sheng module_param(use_dma_mrpc, bool, 0644); 30f7eb7b8aSWesley Sheng MODULE_PARM_DESC(use_dma_mrpc, 31f7eb7b8aSWesley Sheng "Enable the use of the DMA MRPC feature"); 32f7eb7b8aSWesley Sheng 33fcdf8e95SLogan Gunthorpe static int nirqs = 32; 34fcdf8e95SLogan Gunthorpe module_param(nirqs, int, 0644); 35fcdf8e95SLogan Gunthorpe MODULE_PARM_DESC(nirqs, "number of interrupts to allocate (more may be useful for NTB applications)"); 36fcdf8e95SLogan Gunthorpe 37080b47deSLogan Gunthorpe static dev_t switchtec_devt; 38080b47deSLogan Gunthorpe static DEFINE_IDA(switchtec_minor_ida); 39080b47deSLogan Gunthorpe 40302e994dSLogan Gunthorpe struct class *switchtec_class; 41302e994dSLogan Gunthorpe EXPORT_SYMBOL_GPL(switchtec_class); 42080b47deSLogan Gunthorpe 43080b47deSLogan Gunthorpe enum mrpc_state { 44080b47deSLogan Gunthorpe MRPC_IDLE = 0, 45080b47deSLogan Gunthorpe MRPC_QUEUED, 46080b47deSLogan Gunthorpe MRPC_RUNNING, 47080b47deSLogan Gunthorpe MRPC_DONE, 481a323bd0SKelvin Cao MRPC_IO_ERROR, 49080b47deSLogan Gunthorpe }; 50080b47deSLogan Gunthorpe 51080b47deSLogan Gunthorpe struct switchtec_user { 52080b47deSLogan Gunthorpe struct switchtec_dev *stdev; 53080b47deSLogan Gunthorpe 54080b47deSLogan Gunthorpe enum mrpc_state state; 55080b47deSLogan Gunthorpe 56deaa0a8aSSebastian Andrzej Siewior wait_queue_head_t cmd_comp; 57080b47deSLogan Gunthorpe struct kref kref; 58080b47deSLogan Gunthorpe struct list_head list; 59080b47deSLogan Gunthorpe 60deaa0a8aSSebastian Andrzej Siewior bool cmd_done; 61080b47deSLogan Gunthorpe u32 cmd; 62080b47deSLogan Gunthorpe u32 status; 63080b47deSLogan Gunthorpe u32 return_code; 64080b47deSLogan Gunthorpe size_t data_len; 65080b47deSLogan Gunthorpe size_t read_len; 66080b47deSLogan Gunthorpe unsigned char data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; 67080b47deSLogan Gunthorpe int event_cnt; 68080b47deSLogan Gunthorpe }; 69080b47deSLogan Gunthorpe 701a323bd0SKelvin Cao /* 711a323bd0SKelvin Cao * The MMIO reads to the device_id register should always return the device ID 721a323bd0SKelvin Cao * of the device, otherwise the firmware is probably stuck or unreachable 731a323bd0SKelvin Cao * due to a firmware reset which clears PCI state including the BARs and Memory 741a323bd0SKelvin Cao * Space Enable bits. 751a323bd0SKelvin Cao */ 761a323bd0SKelvin Cao static int is_firmware_running(struct switchtec_dev *stdev) 771a323bd0SKelvin Cao { 781a323bd0SKelvin Cao u32 device = ioread32(&stdev->mmio_sys_info->device_id); 791a323bd0SKelvin Cao 801a323bd0SKelvin Cao return stdev->pdev->device == device; 811a323bd0SKelvin Cao } 821a323bd0SKelvin Cao 83080b47deSLogan Gunthorpe static struct switchtec_user *stuser_create(struct switchtec_dev *stdev) 84080b47deSLogan Gunthorpe { 85080b47deSLogan Gunthorpe struct switchtec_user *stuser; 86080b47deSLogan Gunthorpe 87080b47deSLogan Gunthorpe stuser = kzalloc(sizeof(*stuser), GFP_KERNEL); 88080b47deSLogan Gunthorpe if (!stuser) 89080b47deSLogan Gunthorpe return ERR_PTR(-ENOMEM); 90080b47deSLogan Gunthorpe 91080b47deSLogan Gunthorpe get_device(&stdev->dev); 92080b47deSLogan Gunthorpe stuser->stdev = stdev; 93080b47deSLogan Gunthorpe kref_init(&stuser->kref); 94080b47deSLogan Gunthorpe INIT_LIST_HEAD(&stuser->list); 95deaa0a8aSSebastian Andrzej Siewior init_waitqueue_head(&stuser->cmd_comp); 96080b47deSLogan Gunthorpe stuser->event_cnt = atomic_read(&stdev->event_cnt); 97080b47deSLogan Gunthorpe 98080b47deSLogan Gunthorpe dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); 99080b47deSLogan Gunthorpe 100080b47deSLogan Gunthorpe return stuser; 101080b47deSLogan Gunthorpe } 102080b47deSLogan Gunthorpe 103080b47deSLogan Gunthorpe static void stuser_free(struct kref *kref) 104080b47deSLogan Gunthorpe { 105080b47deSLogan Gunthorpe struct switchtec_user *stuser; 106080b47deSLogan Gunthorpe 107080b47deSLogan Gunthorpe stuser = container_of(kref, struct switchtec_user, kref); 108080b47deSLogan Gunthorpe 109080b47deSLogan Gunthorpe dev_dbg(&stuser->stdev->dev, "%s: %p\n", __func__, stuser); 110080b47deSLogan Gunthorpe 111080b47deSLogan Gunthorpe put_device(&stuser->stdev->dev); 112080b47deSLogan Gunthorpe kfree(stuser); 113080b47deSLogan Gunthorpe } 114080b47deSLogan Gunthorpe 115080b47deSLogan Gunthorpe static void stuser_put(struct switchtec_user *stuser) 116080b47deSLogan Gunthorpe { 117080b47deSLogan Gunthorpe kref_put(&stuser->kref, stuser_free); 118080b47deSLogan Gunthorpe } 119080b47deSLogan Gunthorpe 120080b47deSLogan Gunthorpe static void stuser_set_state(struct switchtec_user *stuser, 121080b47deSLogan Gunthorpe enum mrpc_state state) 122080b47deSLogan Gunthorpe { 123080b47deSLogan Gunthorpe /* requires the mrpc_mutex to already be held when called */ 124080b47deSLogan Gunthorpe 125080b47deSLogan Gunthorpe const char * const state_names[] = { 126080b47deSLogan Gunthorpe [MRPC_IDLE] = "IDLE", 127080b47deSLogan Gunthorpe [MRPC_QUEUED] = "QUEUED", 128080b47deSLogan Gunthorpe [MRPC_RUNNING] = "RUNNING", 129080b47deSLogan Gunthorpe [MRPC_DONE] = "DONE", 1301a323bd0SKelvin Cao [MRPC_IO_ERROR] = "IO_ERROR", 131080b47deSLogan Gunthorpe }; 132080b47deSLogan Gunthorpe 133080b47deSLogan Gunthorpe stuser->state = state; 134080b47deSLogan Gunthorpe 135080b47deSLogan Gunthorpe dev_dbg(&stuser->stdev->dev, "stuser state %p -> %s", 136080b47deSLogan Gunthorpe stuser, state_names[state]); 137080b47deSLogan Gunthorpe } 138080b47deSLogan Gunthorpe 139080b47deSLogan Gunthorpe static void mrpc_complete_cmd(struct switchtec_dev *stdev); 140080b47deSLogan Gunthorpe 14152d8db8eSKelvin Cao static void flush_wc_buf(struct switchtec_dev *stdev) 14252d8db8eSKelvin Cao { 14352d8db8eSKelvin Cao struct ntb_dbmsg_regs __iomem *mmio_dbmsg; 14452d8db8eSKelvin Cao 14552d8db8eSKelvin Cao /* 14652d8db8eSKelvin Cao * odb (outbound doorbell) register is processed by low latency 14752d8db8eSKelvin Cao * hardware and w/o side effect 14852d8db8eSKelvin Cao */ 14952d8db8eSKelvin Cao mmio_dbmsg = (void __iomem *)stdev->mmio_ntb + 15052d8db8eSKelvin Cao SWITCHTEC_NTB_REG_DBMSG_OFFSET; 15152d8db8eSKelvin Cao ioread32(&mmio_dbmsg->odb); 15252d8db8eSKelvin Cao } 15352d8db8eSKelvin Cao 154080b47deSLogan Gunthorpe static void mrpc_cmd_submit(struct switchtec_dev *stdev) 155080b47deSLogan Gunthorpe { 156080b47deSLogan Gunthorpe /* requires the mrpc_mutex to already be held when called */ 157080b47deSLogan Gunthorpe 158080b47deSLogan Gunthorpe struct switchtec_user *stuser; 159080b47deSLogan Gunthorpe 160080b47deSLogan Gunthorpe if (stdev->mrpc_busy) 161080b47deSLogan Gunthorpe return; 162080b47deSLogan Gunthorpe 163080b47deSLogan Gunthorpe if (list_empty(&stdev->mrpc_queue)) 164080b47deSLogan Gunthorpe return; 165080b47deSLogan Gunthorpe 166080b47deSLogan Gunthorpe stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, 167080b47deSLogan Gunthorpe list); 168080b47deSLogan Gunthorpe 169f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) { 170f7eb7b8aSWesley Sheng stdev->dma_mrpc->status = SWITCHTEC_MRPC_STATUS_INPROGRESS; 171f7eb7b8aSWesley Sheng memset(stdev->dma_mrpc->data, 0xFF, SWITCHTEC_MRPC_PAYLOAD_SIZE); 172f7eb7b8aSWesley Sheng } 173f7eb7b8aSWesley Sheng 174080b47deSLogan Gunthorpe stuser_set_state(stuser, MRPC_RUNNING); 175080b47deSLogan Gunthorpe stdev->mrpc_busy = 1; 176080b47deSLogan Gunthorpe memcpy_toio(&stdev->mmio_mrpc->input_data, 177080b47deSLogan Gunthorpe stuser->data, stuser->data_len); 17852d8db8eSKelvin Cao flush_wc_buf(stdev); 179080b47deSLogan Gunthorpe iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); 180080b47deSLogan Gunthorpe 181080b47deSLogan Gunthorpe schedule_delayed_work(&stdev->mrpc_timeout, 182080b47deSLogan Gunthorpe msecs_to_jiffies(500)); 183080b47deSLogan Gunthorpe } 184080b47deSLogan Gunthorpe 185080b47deSLogan Gunthorpe static int mrpc_queue_cmd(struct switchtec_user *stuser) 186080b47deSLogan Gunthorpe { 187080b47deSLogan Gunthorpe /* requires the mrpc_mutex to already be held when called */ 188080b47deSLogan Gunthorpe 189080b47deSLogan Gunthorpe struct switchtec_dev *stdev = stuser->stdev; 190080b47deSLogan Gunthorpe 191080b47deSLogan Gunthorpe kref_get(&stuser->kref); 192080b47deSLogan Gunthorpe stuser->read_len = sizeof(stuser->data); 193080b47deSLogan Gunthorpe stuser_set_state(stuser, MRPC_QUEUED); 194deaa0a8aSSebastian Andrzej Siewior stuser->cmd_done = false; 195080b47deSLogan Gunthorpe list_add_tail(&stuser->list, &stdev->mrpc_queue); 196080b47deSLogan Gunthorpe 197080b47deSLogan Gunthorpe mrpc_cmd_submit(stdev); 198080b47deSLogan Gunthorpe 199080b47deSLogan Gunthorpe return 0; 200080b47deSLogan Gunthorpe } 201080b47deSLogan Gunthorpe 2021a323bd0SKelvin Cao static void mrpc_cleanup_cmd(struct switchtec_dev *stdev) 2031a323bd0SKelvin Cao { 2041a323bd0SKelvin Cao /* requires the mrpc_mutex to already be held when called */ 2051a323bd0SKelvin Cao 2061a323bd0SKelvin Cao struct switchtec_user *stuser = list_entry(stdev->mrpc_queue.next, 2071a323bd0SKelvin Cao struct switchtec_user, list); 2081a323bd0SKelvin Cao 2091a323bd0SKelvin Cao stuser->cmd_done = true; 2101a323bd0SKelvin Cao wake_up_interruptible(&stuser->cmd_comp); 2111a323bd0SKelvin Cao list_del_init(&stuser->list); 2121a323bd0SKelvin Cao stuser_put(stuser); 2131a323bd0SKelvin Cao stdev->mrpc_busy = 0; 2141a323bd0SKelvin Cao 2151a323bd0SKelvin Cao mrpc_cmd_submit(stdev); 2161a323bd0SKelvin Cao } 2171a323bd0SKelvin Cao 218080b47deSLogan Gunthorpe static void mrpc_complete_cmd(struct switchtec_dev *stdev) 219080b47deSLogan Gunthorpe { 220080b47deSLogan Gunthorpe /* requires the mrpc_mutex to already be held when called */ 2211a323bd0SKelvin Cao 222080b47deSLogan Gunthorpe struct switchtec_user *stuser; 223080b47deSLogan Gunthorpe 224080b47deSLogan Gunthorpe if (list_empty(&stdev->mrpc_queue)) 225080b47deSLogan Gunthorpe return; 226080b47deSLogan Gunthorpe 227080b47deSLogan Gunthorpe stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, 228080b47deSLogan Gunthorpe list); 229080b47deSLogan Gunthorpe 230f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) 231f7eb7b8aSWesley Sheng stuser->status = stdev->dma_mrpc->status; 232f7eb7b8aSWesley Sheng else 233080b47deSLogan Gunthorpe stuser->status = ioread32(&stdev->mmio_mrpc->status); 234f7eb7b8aSWesley Sheng 235080b47deSLogan Gunthorpe if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS) 236080b47deSLogan Gunthorpe return; 237080b47deSLogan Gunthorpe 238080b47deSLogan Gunthorpe stuser_set_state(stuser, MRPC_DONE); 239080b47deSLogan Gunthorpe stuser->return_code = 0; 240080b47deSLogan Gunthorpe 241*551ec658SKelvin Cao if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE && 242*551ec658SKelvin Cao stuser->status != SWITCHTEC_MRPC_STATUS_ERROR) 243080b47deSLogan Gunthorpe goto out; 244080b47deSLogan Gunthorpe 245f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) 246f7eb7b8aSWesley Sheng stuser->return_code = stdev->dma_mrpc->rtn_code; 247f7eb7b8aSWesley Sheng else 248080b47deSLogan Gunthorpe stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); 249080b47deSLogan Gunthorpe if (stuser->return_code != 0) 250080b47deSLogan Gunthorpe goto out; 251080b47deSLogan Gunthorpe 252f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) 253f7eb7b8aSWesley Sheng memcpy(stuser->data, &stdev->dma_mrpc->data, 254f7eb7b8aSWesley Sheng stuser->read_len); 255f7eb7b8aSWesley Sheng else 256080b47deSLogan Gunthorpe memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, 257080b47deSLogan Gunthorpe stuser->read_len); 258080b47deSLogan Gunthorpe out: 2591a323bd0SKelvin Cao mrpc_cleanup_cmd(stdev); 260080b47deSLogan Gunthorpe } 261080b47deSLogan Gunthorpe 262080b47deSLogan Gunthorpe static void mrpc_event_work(struct work_struct *work) 263080b47deSLogan Gunthorpe { 264080b47deSLogan Gunthorpe struct switchtec_dev *stdev; 265080b47deSLogan Gunthorpe 266080b47deSLogan Gunthorpe stdev = container_of(work, struct switchtec_dev, mrpc_work); 267080b47deSLogan Gunthorpe 268080b47deSLogan Gunthorpe dev_dbg(&stdev->dev, "%s\n", __func__); 269080b47deSLogan Gunthorpe 270080b47deSLogan Gunthorpe mutex_lock(&stdev->mrpc_mutex); 271080b47deSLogan Gunthorpe cancel_delayed_work(&stdev->mrpc_timeout); 272080b47deSLogan Gunthorpe mrpc_complete_cmd(stdev); 273080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 274080b47deSLogan Gunthorpe } 275080b47deSLogan Gunthorpe 2761a323bd0SKelvin Cao static void mrpc_error_complete_cmd(struct switchtec_dev *stdev) 2771a323bd0SKelvin Cao { 2781a323bd0SKelvin Cao /* requires the mrpc_mutex to already be held when called */ 2791a323bd0SKelvin Cao 2801a323bd0SKelvin Cao struct switchtec_user *stuser; 2811a323bd0SKelvin Cao 2821a323bd0SKelvin Cao if (list_empty(&stdev->mrpc_queue)) 2831a323bd0SKelvin Cao return; 2841a323bd0SKelvin Cao 2851a323bd0SKelvin Cao stuser = list_entry(stdev->mrpc_queue.next, 2861a323bd0SKelvin Cao struct switchtec_user, list); 2871a323bd0SKelvin Cao 2881a323bd0SKelvin Cao stuser_set_state(stuser, MRPC_IO_ERROR); 2891a323bd0SKelvin Cao 2901a323bd0SKelvin Cao mrpc_cleanup_cmd(stdev); 2911a323bd0SKelvin Cao } 2921a323bd0SKelvin Cao 293080b47deSLogan Gunthorpe static void mrpc_timeout_work(struct work_struct *work) 294080b47deSLogan Gunthorpe { 295080b47deSLogan Gunthorpe struct switchtec_dev *stdev; 296080b47deSLogan Gunthorpe u32 status; 297080b47deSLogan Gunthorpe 298080b47deSLogan Gunthorpe stdev = container_of(work, struct switchtec_dev, mrpc_timeout.work); 299080b47deSLogan Gunthorpe 300080b47deSLogan Gunthorpe dev_dbg(&stdev->dev, "%s\n", __func__); 301080b47deSLogan Gunthorpe 302080b47deSLogan Gunthorpe mutex_lock(&stdev->mrpc_mutex); 303080b47deSLogan Gunthorpe 3041a323bd0SKelvin Cao if (!is_firmware_running(stdev)) { 3051a323bd0SKelvin Cao mrpc_error_complete_cmd(stdev); 3061a323bd0SKelvin Cao goto out; 3071a323bd0SKelvin Cao } 3081a323bd0SKelvin Cao 309f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) 310f7eb7b8aSWesley Sheng status = stdev->dma_mrpc->status; 311f7eb7b8aSWesley Sheng else 312080b47deSLogan Gunthorpe status = ioread32(&stdev->mmio_mrpc->status); 313080b47deSLogan Gunthorpe if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) { 314080b47deSLogan Gunthorpe schedule_delayed_work(&stdev->mrpc_timeout, 315080b47deSLogan Gunthorpe msecs_to_jiffies(500)); 316080b47deSLogan Gunthorpe goto out; 317080b47deSLogan Gunthorpe } 318080b47deSLogan Gunthorpe 319080b47deSLogan Gunthorpe mrpc_complete_cmd(stdev); 320080b47deSLogan Gunthorpe out: 321080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 322080b47deSLogan Gunthorpe } 323080b47deSLogan Gunthorpe 3245d8e1881SLogan Gunthorpe static ssize_t device_version_show(struct device *dev, 3255d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) 3265d8e1881SLogan Gunthorpe { 3275d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 3285d8e1881SLogan Gunthorpe u32 ver; 3295d8e1881SLogan Gunthorpe 3305d8e1881SLogan Gunthorpe ver = ioread32(&stdev->mmio_sys_info->device_version); 3315d8e1881SLogan Gunthorpe 332f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%x\n", ver); 3335d8e1881SLogan Gunthorpe } 3345d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(device_version); 3355d8e1881SLogan Gunthorpe 3365d8e1881SLogan Gunthorpe static ssize_t fw_version_show(struct device *dev, 3375d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) 3385d8e1881SLogan Gunthorpe { 3395d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 3405d8e1881SLogan Gunthorpe u32 ver; 3415d8e1881SLogan Gunthorpe 3425d8e1881SLogan Gunthorpe ver = ioread32(&stdev->mmio_sys_info->firmware_version); 3435d8e1881SLogan Gunthorpe 344f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%08x\n", ver); 3455d8e1881SLogan Gunthorpe } 3465d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(fw_version); 3475d8e1881SLogan Gunthorpe 3485d8e1881SLogan Gunthorpe static ssize_t io_string_show(char *buf, void __iomem *attr, size_t len) 3495d8e1881SLogan Gunthorpe { 3505d8e1881SLogan Gunthorpe int i; 3515d8e1881SLogan Gunthorpe 3525d8e1881SLogan Gunthorpe memcpy_fromio(buf, attr, len); 3535d8e1881SLogan Gunthorpe buf[len] = '\n'; 3545d8e1881SLogan Gunthorpe buf[len + 1] = 0; 3555d8e1881SLogan Gunthorpe 3565d8e1881SLogan Gunthorpe for (i = len - 1; i > 0; i--) { 3575d8e1881SLogan Gunthorpe if (buf[i] != ' ') 3585d8e1881SLogan Gunthorpe break; 3595d8e1881SLogan Gunthorpe buf[i] = '\n'; 3605d8e1881SLogan Gunthorpe buf[i + 1] = 0; 3615d8e1881SLogan Gunthorpe } 3625d8e1881SLogan Gunthorpe 3635d8e1881SLogan Gunthorpe return strlen(buf); 3645d8e1881SLogan Gunthorpe } 3655d8e1881SLogan Gunthorpe 3665d8e1881SLogan Gunthorpe #define DEVICE_ATTR_SYS_INFO_STR(field) \ 3675d8e1881SLogan Gunthorpe static ssize_t field ## _show(struct device *dev, \ 3685d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) \ 3695d8e1881SLogan Gunthorpe { \ 3705d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); \ 371993d208dSLogan Gunthorpe struct sys_info_regs __iomem *si = stdev->mmio_sys_info; \ 372993d208dSLogan Gunthorpe if (stdev->gen == SWITCHTEC_GEN3) \ 373993d208dSLogan Gunthorpe return io_string_show(buf, &si->gen3.field, \ 374993d208dSLogan Gunthorpe sizeof(si->gen3.field)); \ 375a3321ca3SLogan Gunthorpe else if (stdev->gen == SWITCHTEC_GEN4) \ 376a3321ca3SLogan Gunthorpe return io_string_show(buf, &si->gen4.field, \ 377a3321ca3SLogan Gunthorpe sizeof(si->gen4.field)); \ 378993d208dSLogan Gunthorpe else \ 379993d208dSLogan Gunthorpe return -ENOTSUPP; \ 3805d8e1881SLogan Gunthorpe } \ 3815d8e1881SLogan Gunthorpe \ 3825d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(field) 3835d8e1881SLogan Gunthorpe 3845d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(vendor_id); 3855d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(product_id); 3865d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(product_revision); 387b13313a0SLogan Gunthorpe 388b13313a0SLogan Gunthorpe static ssize_t component_vendor_show(struct device *dev, 389b13313a0SLogan Gunthorpe struct device_attribute *attr, char *buf) 390b13313a0SLogan Gunthorpe { 391b13313a0SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 392b13313a0SLogan Gunthorpe struct sys_info_regs __iomem *si = stdev->mmio_sys_info; 393b13313a0SLogan Gunthorpe 394b13313a0SLogan Gunthorpe /* component_vendor field not supported after gen3 */ 395b13313a0SLogan Gunthorpe if (stdev->gen != SWITCHTEC_GEN3) 396f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "none\n"); 397b13313a0SLogan Gunthorpe 398993d208dSLogan Gunthorpe return io_string_show(buf, &si->gen3.component_vendor, 399993d208dSLogan Gunthorpe sizeof(si->gen3.component_vendor)); 400b13313a0SLogan Gunthorpe } 401b13313a0SLogan Gunthorpe static DEVICE_ATTR_RO(component_vendor); 4025d8e1881SLogan Gunthorpe 4035d8e1881SLogan Gunthorpe static ssize_t component_id_show(struct device *dev, 4045d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) 4055d8e1881SLogan Gunthorpe { 4065d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 407993d208dSLogan Gunthorpe int id = ioread16(&stdev->mmio_sys_info->gen3.component_id); 4085d8e1881SLogan Gunthorpe 409b13313a0SLogan Gunthorpe /* component_id field not supported after gen3 */ 410b13313a0SLogan Gunthorpe if (stdev->gen != SWITCHTEC_GEN3) 411f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "none\n"); 412b13313a0SLogan Gunthorpe 413f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "PM%04X\n", id); 4145d8e1881SLogan Gunthorpe } 4155d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(component_id); 4165d8e1881SLogan Gunthorpe 4175d8e1881SLogan Gunthorpe static ssize_t component_revision_show(struct device *dev, 4185d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) 4195d8e1881SLogan Gunthorpe { 4205d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 421993d208dSLogan Gunthorpe int rev = ioread8(&stdev->mmio_sys_info->gen3.component_revision); 4225d8e1881SLogan Gunthorpe 423b13313a0SLogan Gunthorpe /* component_revision field not supported after gen3 */ 424b13313a0SLogan Gunthorpe if (stdev->gen != SWITCHTEC_GEN3) 425f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "255\n"); 426b13313a0SLogan Gunthorpe 427f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%d\n", rev); 4285d8e1881SLogan Gunthorpe } 4295d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(component_revision); 4305d8e1881SLogan Gunthorpe 4315d8e1881SLogan Gunthorpe static ssize_t partition_show(struct device *dev, 4325d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) 4335d8e1881SLogan Gunthorpe { 4345d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 4355d8e1881SLogan Gunthorpe 436f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%d\n", stdev->partition); 4375d8e1881SLogan Gunthorpe } 4385d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(partition); 4395d8e1881SLogan Gunthorpe 4405d8e1881SLogan Gunthorpe static ssize_t partition_count_show(struct device *dev, 4415d8e1881SLogan Gunthorpe struct device_attribute *attr, char *buf) 4425d8e1881SLogan Gunthorpe { 4435d8e1881SLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 4445d8e1881SLogan Gunthorpe 445f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%d\n", stdev->partition_count); 4465d8e1881SLogan Gunthorpe } 4475d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(partition_count); 4485d8e1881SLogan Gunthorpe 4495d8e1881SLogan Gunthorpe static struct attribute *switchtec_device_attrs[] = { 4505d8e1881SLogan Gunthorpe &dev_attr_device_version.attr, 4515d8e1881SLogan Gunthorpe &dev_attr_fw_version.attr, 4525d8e1881SLogan Gunthorpe &dev_attr_vendor_id.attr, 4535d8e1881SLogan Gunthorpe &dev_attr_product_id.attr, 4545d8e1881SLogan Gunthorpe &dev_attr_product_revision.attr, 4555d8e1881SLogan Gunthorpe &dev_attr_component_vendor.attr, 4565d8e1881SLogan Gunthorpe &dev_attr_component_id.attr, 4575d8e1881SLogan Gunthorpe &dev_attr_component_revision.attr, 4585d8e1881SLogan Gunthorpe &dev_attr_partition.attr, 4595d8e1881SLogan Gunthorpe &dev_attr_partition_count.attr, 4605d8e1881SLogan Gunthorpe NULL, 4615d8e1881SLogan Gunthorpe }; 4625d8e1881SLogan Gunthorpe 4635d8e1881SLogan Gunthorpe ATTRIBUTE_GROUPS(switchtec_device); 4645d8e1881SLogan Gunthorpe 465080b47deSLogan Gunthorpe static int switchtec_dev_open(struct inode *inode, struct file *filp) 466080b47deSLogan Gunthorpe { 467080b47deSLogan Gunthorpe struct switchtec_dev *stdev; 468080b47deSLogan Gunthorpe struct switchtec_user *stuser; 469080b47deSLogan Gunthorpe 470080b47deSLogan Gunthorpe stdev = container_of(inode->i_cdev, struct switchtec_dev, cdev); 471080b47deSLogan Gunthorpe 472080b47deSLogan Gunthorpe stuser = stuser_create(stdev); 473080b47deSLogan Gunthorpe if (IS_ERR(stuser)) 474080b47deSLogan Gunthorpe return PTR_ERR(stuser); 475080b47deSLogan Gunthorpe 476080b47deSLogan Gunthorpe filp->private_data = stuser; 477c5bf68feSKirill Smelkov stream_open(inode, filp); 478080b47deSLogan Gunthorpe 479080b47deSLogan Gunthorpe dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); 480080b47deSLogan Gunthorpe 481080b47deSLogan Gunthorpe return 0; 482080b47deSLogan Gunthorpe } 483080b47deSLogan Gunthorpe 484080b47deSLogan Gunthorpe static int switchtec_dev_release(struct inode *inode, struct file *filp) 485080b47deSLogan Gunthorpe { 486080b47deSLogan Gunthorpe struct switchtec_user *stuser = filp->private_data; 487080b47deSLogan Gunthorpe 488080b47deSLogan Gunthorpe stuser_put(stuser); 489080b47deSLogan Gunthorpe 490080b47deSLogan Gunthorpe return 0; 491080b47deSLogan Gunthorpe } 492080b47deSLogan Gunthorpe 493080b47deSLogan Gunthorpe static int lock_mutex_and_test_alive(struct switchtec_dev *stdev) 494080b47deSLogan Gunthorpe { 495080b47deSLogan Gunthorpe if (mutex_lock_interruptible(&stdev->mrpc_mutex)) 496080b47deSLogan Gunthorpe return -EINTR; 497080b47deSLogan Gunthorpe 498080b47deSLogan Gunthorpe if (!stdev->alive) { 499080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 500080b47deSLogan Gunthorpe return -ENODEV; 501080b47deSLogan Gunthorpe } 502080b47deSLogan Gunthorpe 503080b47deSLogan Gunthorpe return 0; 504080b47deSLogan Gunthorpe } 505080b47deSLogan Gunthorpe 506080b47deSLogan Gunthorpe static ssize_t switchtec_dev_write(struct file *filp, const char __user *data, 507080b47deSLogan Gunthorpe size_t size, loff_t *off) 508080b47deSLogan Gunthorpe { 509080b47deSLogan Gunthorpe struct switchtec_user *stuser = filp->private_data; 510080b47deSLogan Gunthorpe struct switchtec_dev *stdev = stuser->stdev; 511080b47deSLogan Gunthorpe int rc; 512080b47deSLogan Gunthorpe 513080b47deSLogan Gunthorpe if (size < sizeof(stuser->cmd) || 514080b47deSLogan Gunthorpe size > sizeof(stuser->cmd) + sizeof(stuser->data)) 515080b47deSLogan Gunthorpe return -EINVAL; 516080b47deSLogan Gunthorpe 517080b47deSLogan Gunthorpe stuser->data_len = size - sizeof(stuser->cmd); 518080b47deSLogan Gunthorpe 519080b47deSLogan Gunthorpe rc = lock_mutex_and_test_alive(stdev); 520080b47deSLogan Gunthorpe if (rc) 521080b47deSLogan Gunthorpe return rc; 522080b47deSLogan Gunthorpe 523080b47deSLogan Gunthorpe if (stuser->state != MRPC_IDLE) { 524080b47deSLogan Gunthorpe rc = -EBADE; 525080b47deSLogan Gunthorpe goto out; 526080b47deSLogan Gunthorpe } 527080b47deSLogan Gunthorpe 528080b47deSLogan Gunthorpe rc = copy_from_user(&stuser->cmd, data, sizeof(stuser->cmd)); 529080b47deSLogan Gunthorpe if (rc) { 530080b47deSLogan Gunthorpe rc = -EFAULT; 531080b47deSLogan Gunthorpe goto out; 532080b47deSLogan Gunthorpe } 533ce7c8860SKelvin Cao if (((MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_WRITE) || 534ce7c8860SKelvin Cao (MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_READ)) && 535ce7c8860SKelvin Cao !capable(CAP_SYS_ADMIN)) { 536ce7c8860SKelvin Cao rc = -EPERM; 537ce7c8860SKelvin Cao goto out; 538ce7c8860SKelvin Cao } 539080b47deSLogan Gunthorpe 540080b47deSLogan Gunthorpe data += sizeof(stuser->cmd); 541080b47deSLogan Gunthorpe rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd)); 542080b47deSLogan Gunthorpe if (rc) { 543080b47deSLogan Gunthorpe rc = -EFAULT; 544080b47deSLogan Gunthorpe goto out; 545080b47deSLogan Gunthorpe } 546080b47deSLogan Gunthorpe 547080b47deSLogan Gunthorpe rc = mrpc_queue_cmd(stuser); 548080b47deSLogan Gunthorpe 549080b47deSLogan Gunthorpe out: 550080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 551080b47deSLogan Gunthorpe 552080b47deSLogan Gunthorpe if (rc) 553080b47deSLogan Gunthorpe return rc; 554080b47deSLogan Gunthorpe 555080b47deSLogan Gunthorpe return size; 556080b47deSLogan Gunthorpe } 557080b47deSLogan Gunthorpe 558080b47deSLogan Gunthorpe static ssize_t switchtec_dev_read(struct file *filp, char __user *data, 559080b47deSLogan Gunthorpe size_t size, loff_t *off) 560080b47deSLogan Gunthorpe { 561080b47deSLogan Gunthorpe struct switchtec_user *stuser = filp->private_data; 562080b47deSLogan Gunthorpe struct switchtec_dev *stdev = stuser->stdev; 563080b47deSLogan Gunthorpe int rc; 564080b47deSLogan Gunthorpe 565080b47deSLogan Gunthorpe if (size < sizeof(stuser->cmd) || 566080b47deSLogan Gunthorpe size > sizeof(stuser->cmd) + sizeof(stuser->data)) 567080b47deSLogan Gunthorpe return -EINVAL; 568080b47deSLogan Gunthorpe 569080b47deSLogan Gunthorpe rc = lock_mutex_and_test_alive(stdev); 570080b47deSLogan Gunthorpe if (rc) 571080b47deSLogan Gunthorpe return rc; 572080b47deSLogan Gunthorpe 573080b47deSLogan Gunthorpe if (stuser->state == MRPC_IDLE) { 574080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 575080b47deSLogan Gunthorpe return -EBADE; 576080b47deSLogan Gunthorpe } 577080b47deSLogan Gunthorpe 578080b47deSLogan Gunthorpe stuser->read_len = size - sizeof(stuser->return_code); 579080b47deSLogan Gunthorpe 580080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 581080b47deSLogan Gunthorpe 582080b47deSLogan Gunthorpe if (filp->f_flags & O_NONBLOCK) { 583deaa0a8aSSebastian Andrzej Siewior if (!stuser->cmd_done) 584080b47deSLogan Gunthorpe return -EAGAIN; 585080b47deSLogan Gunthorpe } else { 586deaa0a8aSSebastian Andrzej Siewior rc = wait_event_interruptible(stuser->cmd_comp, 587deaa0a8aSSebastian Andrzej Siewior stuser->cmd_done); 588080b47deSLogan Gunthorpe if (rc < 0) 589080b47deSLogan Gunthorpe return rc; 590080b47deSLogan Gunthorpe } 591080b47deSLogan Gunthorpe 592080b47deSLogan Gunthorpe rc = lock_mutex_and_test_alive(stdev); 593080b47deSLogan Gunthorpe if (rc) 594080b47deSLogan Gunthorpe return rc; 595080b47deSLogan Gunthorpe 5961a323bd0SKelvin Cao if (stuser->state == MRPC_IO_ERROR) { 5971a323bd0SKelvin Cao mutex_unlock(&stdev->mrpc_mutex); 5981a323bd0SKelvin Cao return -EIO; 5991a323bd0SKelvin Cao } 6001a323bd0SKelvin Cao 601080b47deSLogan Gunthorpe if (stuser->state != MRPC_DONE) { 602080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 603080b47deSLogan Gunthorpe return -EBADE; 604080b47deSLogan Gunthorpe } 605080b47deSLogan Gunthorpe 606080b47deSLogan Gunthorpe rc = copy_to_user(data, &stuser->return_code, 607080b47deSLogan Gunthorpe sizeof(stuser->return_code)); 608080b47deSLogan Gunthorpe if (rc) { 609080b47deSLogan Gunthorpe rc = -EFAULT; 610080b47deSLogan Gunthorpe goto out; 611080b47deSLogan Gunthorpe } 612080b47deSLogan Gunthorpe 613080b47deSLogan Gunthorpe data += sizeof(stuser->return_code); 614080b47deSLogan Gunthorpe rc = copy_to_user(data, &stuser->data, 615080b47deSLogan Gunthorpe size - sizeof(stuser->return_code)); 616080b47deSLogan Gunthorpe if (rc) { 617080b47deSLogan Gunthorpe rc = -EFAULT; 618080b47deSLogan Gunthorpe goto out; 619080b47deSLogan Gunthorpe } 620080b47deSLogan Gunthorpe 621080b47deSLogan Gunthorpe stuser_set_state(stuser, MRPC_IDLE); 622080b47deSLogan Gunthorpe 623080b47deSLogan Gunthorpe out: 624080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 625080b47deSLogan Gunthorpe 626*551ec658SKelvin Cao if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE || 627*551ec658SKelvin Cao stuser->status == SWITCHTEC_MRPC_STATUS_ERROR) 628080b47deSLogan Gunthorpe return size; 629080b47deSLogan Gunthorpe else if (stuser->status == SWITCHTEC_MRPC_STATUS_INTERRUPTED) 630080b47deSLogan Gunthorpe return -ENXIO; 631080b47deSLogan Gunthorpe else 632080b47deSLogan Gunthorpe return -EBADMSG; 633080b47deSLogan Gunthorpe } 634080b47deSLogan Gunthorpe 635afc9a42bSAl Viro static __poll_t switchtec_dev_poll(struct file *filp, poll_table *wait) 636080b47deSLogan Gunthorpe { 637080b47deSLogan Gunthorpe struct switchtec_user *stuser = filp->private_data; 638080b47deSLogan Gunthorpe struct switchtec_dev *stdev = stuser->stdev; 639afc9a42bSAl Viro __poll_t ret = 0; 640080b47deSLogan Gunthorpe 641deaa0a8aSSebastian Andrzej Siewior poll_wait(filp, &stuser->cmd_comp, wait); 642080b47deSLogan Gunthorpe poll_wait(filp, &stdev->event_wq, wait); 643080b47deSLogan Gunthorpe 644080b47deSLogan Gunthorpe if (lock_mutex_and_test_alive(stdev)) 645a9a08845SLinus Torvalds return EPOLLIN | EPOLLRDHUP | EPOLLOUT | EPOLLERR | EPOLLHUP; 646080b47deSLogan Gunthorpe 647080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 648080b47deSLogan Gunthorpe 649deaa0a8aSSebastian Andrzej Siewior if (stuser->cmd_done) 650a9a08845SLinus Torvalds ret |= EPOLLIN | EPOLLRDNORM; 651080b47deSLogan Gunthorpe 652080b47deSLogan Gunthorpe if (stuser->event_cnt != atomic_read(&stdev->event_cnt)) 653a9a08845SLinus Torvalds ret |= EPOLLPRI | EPOLLRDBAND; 654080b47deSLogan Gunthorpe 655080b47deSLogan Gunthorpe return ret; 656080b47deSLogan Gunthorpe } 657080b47deSLogan Gunthorpe 65852eabba5SLogan Gunthorpe static int ioctl_flash_info(struct switchtec_dev *stdev, 65952eabba5SLogan Gunthorpe struct switchtec_ioctl_flash_info __user *uinfo) 66052eabba5SLogan Gunthorpe { 66152eabba5SLogan Gunthorpe struct switchtec_ioctl_flash_info info = {0}; 66252eabba5SLogan Gunthorpe struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; 66352eabba5SLogan Gunthorpe 664993d208dSLogan Gunthorpe if (stdev->gen == SWITCHTEC_GEN3) { 665993d208dSLogan Gunthorpe info.flash_length = ioread32(&fi->gen3.flash_length); 666fcccd282SLogan Gunthorpe info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; 6674efa1d2eSKelvin Cao } else if (stdev->gen == SWITCHTEC_GEN4) { 6684efa1d2eSKelvin Cao info.flash_length = ioread32(&fi->gen4.flash_length); 6694efa1d2eSKelvin Cao info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN4; 670993d208dSLogan Gunthorpe } else { 671993d208dSLogan Gunthorpe return -ENOTSUPP; 672993d208dSLogan Gunthorpe } 67352eabba5SLogan Gunthorpe 67452eabba5SLogan Gunthorpe if (copy_to_user(uinfo, &info, sizeof(info))) 67552eabba5SLogan Gunthorpe return -EFAULT; 67652eabba5SLogan Gunthorpe 67752eabba5SLogan Gunthorpe return 0; 67852eabba5SLogan Gunthorpe } 67952eabba5SLogan Gunthorpe 68052eabba5SLogan Gunthorpe static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, 68152eabba5SLogan Gunthorpe struct partition_info __iomem *pi) 68252eabba5SLogan Gunthorpe { 68352eabba5SLogan Gunthorpe info->address = ioread32(&pi->address); 68452eabba5SLogan Gunthorpe info->length = ioread32(&pi->length); 68552eabba5SLogan Gunthorpe } 68652eabba5SLogan Gunthorpe 6876a3d1b54SLogan Gunthorpe static int flash_part_info_gen3(struct switchtec_dev *stdev, 6886a3d1b54SLogan Gunthorpe struct switchtec_ioctl_flash_part_info *info) 68952eabba5SLogan Gunthorpe { 690993d208dSLogan Gunthorpe struct flash_info_regs_gen3 __iomem *fi = 691993d208dSLogan Gunthorpe &stdev->mmio_flash_info->gen3; 692993d208dSLogan Gunthorpe struct sys_info_regs_gen3 __iomem *si = &stdev->mmio_sys_info->gen3; 69352eabba5SLogan Gunthorpe u32 active_addr = -1; 69452eabba5SLogan Gunthorpe 6956a3d1b54SLogan Gunthorpe switch (info->flash_partition) { 69652eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_CFG0: 69752eabba5SLogan Gunthorpe active_addr = ioread32(&fi->active_cfg); 6986a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->cfg0); 699fcccd282SLogan Gunthorpe if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG0_RUNNING) 7006a3d1b54SLogan Gunthorpe info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 70152eabba5SLogan Gunthorpe break; 70252eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_CFG1: 70352eabba5SLogan Gunthorpe active_addr = ioread32(&fi->active_cfg); 7046a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->cfg1); 705fcccd282SLogan Gunthorpe if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG1_RUNNING) 7066a3d1b54SLogan Gunthorpe info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 70752eabba5SLogan Gunthorpe break; 70852eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_IMG0: 70952eabba5SLogan Gunthorpe active_addr = ioread32(&fi->active_img); 7106a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->img0); 711fcccd282SLogan Gunthorpe if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG0_RUNNING) 7126a3d1b54SLogan Gunthorpe info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 71352eabba5SLogan Gunthorpe break; 71452eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_IMG1: 71552eabba5SLogan Gunthorpe active_addr = ioread32(&fi->active_img); 7166a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->img1); 717fcccd282SLogan Gunthorpe if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG1_RUNNING) 7186a3d1b54SLogan Gunthorpe info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 71952eabba5SLogan Gunthorpe break; 72052eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_NVLOG: 7216a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->nvlog); 72252eabba5SLogan Gunthorpe break; 72352eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR0: 7246a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[0]); 72552eabba5SLogan Gunthorpe break; 72652eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR1: 7276a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[1]); 72852eabba5SLogan Gunthorpe break; 72952eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR2: 7306a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[2]); 73152eabba5SLogan Gunthorpe break; 73252eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR3: 7336a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[3]); 73452eabba5SLogan Gunthorpe break; 73552eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR4: 7366a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[4]); 73752eabba5SLogan Gunthorpe break; 73852eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR5: 7396a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[5]); 74052eabba5SLogan Gunthorpe break; 74152eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR6: 7426a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[6]); 74352eabba5SLogan Gunthorpe break; 74452eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PART_VENDOR7: 7456a3d1b54SLogan Gunthorpe set_fw_info_part(info, &fi->vendor[7]); 74652eabba5SLogan Gunthorpe break; 74752eabba5SLogan Gunthorpe default: 74852eabba5SLogan Gunthorpe return -EINVAL; 74952eabba5SLogan Gunthorpe } 75052eabba5SLogan Gunthorpe 7516a3d1b54SLogan Gunthorpe if (info->address == active_addr) 7526a3d1b54SLogan Gunthorpe info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 7536a3d1b54SLogan Gunthorpe 7546a3d1b54SLogan Gunthorpe return 0; 7556a3d1b54SLogan Gunthorpe } 7566a3d1b54SLogan Gunthorpe 7574efa1d2eSKelvin Cao static int flash_part_info_gen4(struct switchtec_dev *stdev, 7584efa1d2eSKelvin Cao struct switchtec_ioctl_flash_part_info *info) 7594efa1d2eSKelvin Cao { 7604efa1d2eSKelvin Cao struct flash_info_regs_gen4 __iomem *fi = &stdev->mmio_flash_info->gen4; 7614efa1d2eSKelvin Cao struct sys_info_regs_gen4 __iomem *si = &stdev->mmio_sys_info->gen4; 7624efa1d2eSKelvin Cao struct active_partition_info_gen4 __iomem *af = &fi->active_flag; 7634efa1d2eSKelvin Cao 7644efa1d2eSKelvin Cao switch (info->flash_partition) { 7654efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_MAP_0: 7664efa1d2eSKelvin Cao set_fw_info_part(info, &fi->map0); 7674efa1d2eSKelvin Cao break; 7684efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_MAP_1: 7694efa1d2eSKelvin Cao set_fw_info_part(info, &fi->map1); 7704efa1d2eSKelvin Cao break; 7714efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_KEY_0: 7724efa1d2eSKelvin Cao set_fw_info_part(info, &fi->key0); 7734efa1d2eSKelvin Cao if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY0_ACTIVE) 7744efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 7754efa1d2eSKelvin Cao if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY0_RUNNING) 7764efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 7774efa1d2eSKelvin Cao break; 7784efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_KEY_1: 7794efa1d2eSKelvin Cao set_fw_info_part(info, &fi->key1); 7804efa1d2eSKelvin Cao if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY1_ACTIVE) 7814efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 7824efa1d2eSKelvin Cao if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY1_RUNNING) 7834efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 7844efa1d2eSKelvin Cao break; 7854efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_BL2_0: 7864efa1d2eSKelvin Cao set_fw_info_part(info, &fi->bl2_0); 7874efa1d2eSKelvin Cao if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_0_ACTIVE) 7884efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 7894efa1d2eSKelvin Cao if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_0_RUNNING) 7904efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 7914efa1d2eSKelvin Cao break; 7924efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_BL2_1: 7934efa1d2eSKelvin Cao set_fw_info_part(info, &fi->bl2_1); 7944efa1d2eSKelvin Cao if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_1_ACTIVE) 7954efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 7964efa1d2eSKelvin Cao if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_1_RUNNING) 7974efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 7984efa1d2eSKelvin Cao break; 7994efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_CFG0: 8004efa1d2eSKelvin Cao set_fw_info_part(info, &fi->cfg0); 8014efa1d2eSKelvin Cao if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG0_ACTIVE) 8024efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 8034efa1d2eSKelvin Cao if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG0_RUNNING) 8044efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 8054efa1d2eSKelvin Cao break; 8064efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_CFG1: 8074efa1d2eSKelvin Cao set_fw_info_part(info, &fi->cfg1); 8084efa1d2eSKelvin Cao if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG1_ACTIVE) 8094efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 8104efa1d2eSKelvin Cao if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG1_RUNNING) 8114efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 8124efa1d2eSKelvin Cao break; 8134efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_IMG0: 8144efa1d2eSKelvin Cao set_fw_info_part(info, &fi->img0); 8154efa1d2eSKelvin Cao if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG0_ACTIVE) 8164efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 8174efa1d2eSKelvin Cao if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG0_RUNNING) 8184efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 8194efa1d2eSKelvin Cao break; 8204efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_IMG1: 8214efa1d2eSKelvin Cao set_fw_info_part(info, &fi->img1); 8224efa1d2eSKelvin Cao if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG1_ACTIVE) 8234efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; 8244efa1d2eSKelvin Cao if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG1_RUNNING) 8254efa1d2eSKelvin Cao info->active |= SWITCHTEC_IOCTL_PART_RUNNING; 8264efa1d2eSKelvin Cao break; 8274efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_NVLOG: 8284efa1d2eSKelvin Cao set_fw_info_part(info, &fi->nvlog); 8294efa1d2eSKelvin Cao break; 8304efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR0: 8314efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[0]); 8324efa1d2eSKelvin Cao break; 8334efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR1: 8344efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[1]); 8354efa1d2eSKelvin Cao break; 8364efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR2: 8374efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[2]); 8384efa1d2eSKelvin Cao break; 8394efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR3: 8404efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[3]); 8414efa1d2eSKelvin Cao break; 8424efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR4: 8434efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[4]); 8444efa1d2eSKelvin Cao break; 8454efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR5: 8464efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[5]); 8474efa1d2eSKelvin Cao break; 8484efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR6: 8494efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[6]); 8504efa1d2eSKelvin Cao break; 8514efa1d2eSKelvin Cao case SWITCHTEC_IOCTL_PART_VENDOR7: 8524efa1d2eSKelvin Cao set_fw_info_part(info, &fi->vendor[7]); 8534efa1d2eSKelvin Cao break; 8544efa1d2eSKelvin Cao default: 8554efa1d2eSKelvin Cao return -EINVAL; 8564efa1d2eSKelvin Cao } 8574efa1d2eSKelvin Cao 8584efa1d2eSKelvin Cao return 0; 8594efa1d2eSKelvin Cao } 8604efa1d2eSKelvin Cao 8616a3d1b54SLogan Gunthorpe static int ioctl_flash_part_info(struct switchtec_dev *stdev, 8626a3d1b54SLogan Gunthorpe struct switchtec_ioctl_flash_part_info __user *uinfo) 8636a3d1b54SLogan Gunthorpe { 8646a3d1b54SLogan Gunthorpe int ret; 8656a3d1b54SLogan Gunthorpe struct switchtec_ioctl_flash_part_info info = {0}; 8666a3d1b54SLogan Gunthorpe 8676a3d1b54SLogan Gunthorpe if (copy_from_user(&info, uinfo, sizeof(info))) 8686a3d1b54SLogan Gunthorpe return -EFAULT; 8696a3d1b54SLogan Gunthorpe 8706a3d1b54SLogan Gunthorpe if (stdev->gen == SWITCHTEC_GEN3) { 8716a3d1b54SLogan Gunthorpe ret = flash_part_info_gen3(stdev, &info); 8726a3d1b54SLogan Gunthorpe if (ret) 8736a3d1b54SLogan Gunthorpe return ret; 8744efa1d2eSKelvin Cao } else if (stdev->gen == SWITCHTEC_GEN4) { 8754efa1d2eSKelvin Cao ret = flash_part_info_gen4(stdev, &info); 8764efa1d2eSKelvin Cao if (ret) 8774efa1d2eSKelvin Cao return ret; 8786a3d1b54SLogan Gunthorpe } else { 8796a3d1b54SLogan Gunthorpe return -ENOTSUPP; 8806a3d1b54SLogan Gunthorpe } 88152eabba5SLogan Gunthorpe 88252eabba5SLogan Gunthorpe if (copy_to_user(uinfo, &info, sizeof(info))) 88352eabba5SLogan Gunthorpe return -EFAULT; 88452eabba5SLogan Gunthorpe 88552eabba5SLogan Gunthorpe return 0; 88652eabba5SLogan Gunthorpe } 88752eabba5SLogan Gunthorpe 88852eabba5SLogan Gunthorpe static int ioctl_event_summary(struct switchtec_dev *stdev, 88952eabba5SLogan Gunthorpe struct switchtec_user *stuser, 890ba8a3982SWesley Sheng struct switchtec_ioctl_event_summary __user *usum, 891ba8a3982SWesley Sheng size_t size) 89252eabba5SLogan Gunthorpe { 893ba8a3982SWesley Sheng struct switchtec_ioctl_event_summary *s; 89452eabba5SLogan Gunthorpe int i; 89552eabba5SLogan Gunthorpe u32 reg; 896ba8a3982SWesley Sheng int ret = 0; 89752eabba5SLogan Gunthorpe 898ba8a3982SWesley Sheng s = kzalloc(sizeof(*s), GFP_KERNEL); 899ba8a3982SWesley Sheng if (!s) 900ba8a3982SWesley Sheng return -ENOMEM; 901ba8a3982SWesley Sheng 902ba8a3982SWesley Sheng s->global = ioread32(&stdev->mmio_sw_event->global_summary); 9036acdf7e1SLogan Gunthorpe s->part_bitmap = ioread64(&stdev->mmio_sw_event->part_event_bitmap); 904ba8a3982SWesley Sheng s->local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary); 90552eabba5SLogan Gunthorpe 90652eabba5SLogan Gunthorpe for (i = 0; i < stdev->partition_count; i++) { 90752eabba5SLogan Gunthorpe reg = ioread32(&stdev->mmio_part_cfg_all[i].part_event_summary); 908ba8a3982SWesley Sheng s->part[i] = reg; 90952eabba5SLogan Gunthorpe } 91052eabba5SLogan Gunthorpe 9117501a02aSWesley Sheng for (i = 0; i < stdev->pff_csr_count; i++) { 91252eabba5SLogan Gunthorpe reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary); 913ba8a3982SWesley Sheng s->pff[i] = reg; 91452eabba5SLogan Gunthorpe } 91552eabba5SLogan Gunthorpe 916ba8a3982SWesley Sheng if (copy_to_user(usum, s, size)) { 917ba8a3982SWesley Sheng ret = -EFAULT; 918ba8a3982SWesley Sheng goto error_case; 919ba8a3982SWesley Sheng } 92052eabba5SLogan Gunthorpe 92152eabba5SLogan Gunthorpe stuser->event_cnt = atomic_read(&stdev->event_cnt); 92252eabba5SLogan Gunthorpe 923ba8a3982SWesley Sheng error_case: 924ba8a3982SWesley Sheng kfree(s); 925ba8a3982SWesley Sheng return ret; 92652eabba5SLogan Gunthorpe } 92752eabba5SLogan Gunthorpe 92852eabba5SLogan Gunthorpe static u32 __iomem *global_ev_reg(struct switchtec_dev *stdev, 92952eabba5SLogan Gunthorpe size_t offset, int index) 93052eabba5SLogan Gunthorpe { 93152eabba5SLogan Gunthorpe return (void __iomem *)stdev->mmio_sw_event + offset; 93252eabba5SLogan Gunthorpe } 93352eabba5SLogan Gunthorpe 93452eabba5SLogan Gunthorpe static u32 __iomem *part_ev_reg(struct switchtec_dev *stdev, 93552eabba5SLogan Gunthorpe size_t offset, int index) 93652eabba5SLogan Gunthorpe { 93752eabba5SLogan Gunthorpe return (void __iomem *)&stdev->mmio_part_cfg_all[index] + offset; 93852eabba5SLogan Gunthorpe } 93952eabba5SLogan Gunthorpe 94052eabba5SLogan Gunthorpe static u32 __iomem *pff_ev_reg(struct switchtec_dev *stdev, 94152eabba5SLogan Gunthorpe size_t offset, int index) 94252eabba5SLogan Gunthorpe { 94352eabba5SLogan Gunthorpe return (void __iomem *)&stdev->mmio_pff_csr[index] + offset; 94452eabba5SLogan Gunthorpe } 94552eabba5SLogan Gunthorpe 94652eabba5SLogan Gunthorpe #define EV_GLB(i, r)[i] = {offsetof(struct sw_event_regs, r), global_ev_reg} 94752eabba5SLogan Gunthorpe #define EV_PAR(i, r)[i] = {offsetof(struct part_cfg_regs, r), part_ev_reg} 94852eabba5SLogan Gunthorpe #define EV_PFF(i, r)[i] = {offsetof(struct pff_csr_regs, r), pff_ev_reg} 94952eabba5SLogan Gunthorpe 950f05f7355SColin Ian King static const struct event_reg { 95152eabba5SLogan Gunthorpe size_t offset; 95252eabba5SLogan Gunthorpe u32 __iomem *(*map_reg)(struct switchtec_dev *stdev, 95352eabba5SLogan Gunthorpe size_t offset, int index); 95452eabba5SLogan Gunthorpe } event_regs[] = { 95552eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_STACK_ERROR, stack_error_event_hdr), 95652eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_PPU_ERROR, ppu_error_event_hdr), 95752eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_ISP_ERROR, isp_error_event_hdr), 95852eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_SYS_RESET, sys_reset_event_hdr), 95952eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_EXC, fw_exception_hdr), 96052eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NMI, fw_nmi_hdr), 96152eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL, fw_non_fatal_hdr), 96252eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_FATAL, fw_fatal_hdr), 96352eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP, twi_mrpc_comp_hdr), 96452eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC, 96552eabba5SLogan Gunthorpe twi_mrpc_comp_async_hdr), 96652eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP, cli_mrpc_comp_hdr), 96752eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC, 96852eabba5SLogan Gunthorpe cli_mrpc_comp_async_hdr), 96952eabba5SLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_GPIO_INT, gpio_interrupt_hdr), 970f0edce7aSLogan Gunthorpe EV_GLB(SWITCHTEC_IOCTL_EVENT_GFMS, gfms_event_hdr), 97152eabba5SLogan Gunthorpe EV_PAR(SWITCHTEC_IOCTL_EVENT_PART_RESET, part_reset_hdr), 97252eabba5SLogan Gunthorpe EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr), 97352eabba5SLogan Gunthorpe EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr), 97452eabba5SLogan Gunthorpe EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr), 975a6b0ef9aSLogan Gunthorpe EV_PAR(SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY, 976a6b0ef9aSLogan Gunthorpe intercomm_notify_hdr), 97752eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr), 97852eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr), 97952eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr), 98052eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr), 981a6b0ef9aSLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_UEC, uec_hdr), 98252eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr), 98352eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr), 98452eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr), 98552eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_POWER_MGMT, power_mgmt_hdr), 98652eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING, tlp_throttling_hdr), 98752eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_FORCE_SPEED, force_speed_hdr), 98852eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT, credit_timeout_hdr), 98952eabba5SLogan Gunthorpe EV_PFF(SWITCHTEC_IOCTL_EVENT_LINK_STATE, link_state_hdr), 99052eabba5SLogan Gunthorpe }; 99152eabba5SLogan Gunthorpe 99252eabba5SLogan Gunthorpe static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev, 99352eabba5SLogan Gunthorpe int event_id, int index) 99452eabba5SLogan Gunthorpe { 99552eabba5SLogan Gunthorpe size_t off; 99652eabba5SLogan Gunthorpe 99752eabba5SLogan Gunthorpe if (event_id < 0 || event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) 9985f11723bSLogan Gunthorpe return (u32 __iomem *)ERR_PTR(-EINVAL); 99952eabba5SLogan Gunthorpe 100052eabba5SLogan Gunthorpe off = event_regs[event_id].offset; 100152eabba5SLogan Gunthorpe 100252eabba5SLogan Gunthorpe if (event_regs[event_id].map_reg == part_ev_reg) { 100352eabba5SLogan Gunthorpe if (index == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) 100452eabba5SLogan Gunthorpe index = stdev->partition; 100552eabba5SLogan Gunthorpe else if (index < 0 || index >= stdev->partition_count) 10065f11723bSLogan Gunthorpe return (u32 __iomem *)ERR_PTR(-EINVAL); 100752eabba5SLogan Gunthorpe } else if (event_regs[event_id].map_reg == pff_ev_reg) { 100852eabba5SLogan Gunthorpe if (index < 0 || index >= stdev->pff_csr_count) 10095f11723bSLogan Gunthorpe return (u32 __iomem *)ERR_PTR(-EINVAL); 101052eabba5SLogan Gunthorpe } 101152eabba5SLogan Gunthorpe 101252eabba5SLogan Gunthorpe return event_regs[event_id].map_reg(stdev, off, index); 101352eabba5SLogan Gunthorpe } 101452eabba5SLogan Gunthorpe 101552eabba5SLogan Gunthorpe static int event_ctl(struct switchtec_dev *stdev, 101652eabba5SLogan Gunthorpe struct switchtec_ioctl_event_ctl *ctl) 101752eabba5SLogan Gunthorpe { 101852eabba5SLogan Gunthorpe int i; 101952eabba5SLogan Gunthorpe u32 __iomem *reg; 102052eabba5SLogan Gunthorpe u32 hdr; 102152eabba5SLogan Gunthorpe 102252eabba5SLogan Gunthorpe reg = event_hdr_addr(stdev, ctl->event_id, ctl->index); 102352eabba5SLogan Gunthorpe if (IS_ERR(reg)) 102452eabba5SLogan Gunthorpe return PTR_ERR(reg); 102552eabba5SLogan Gunthorpe 102652eabba5SLogan Gunthorpe hdr = ioread32(reg); 102752eabba5SLogan Gunthorpe for (i = 0; i < ARRAY_SIZE(ctl->data); i++) 102852eabba5SLogan Gunthorpe ctl->data[i] = ioread32(®[i + 1]); 102952eabba5SLogan Gunthorpe 103052eabba5SLogan Gunthorpe ctl->occurred = hdr & SWITCHTEC_EVENT_OCCURRED; 103152eabba5SLogan Gunthorpe ctl->count = (hdr >> 5) & 0xFF; 103252eabba5SLogan Gunthorpe 103352eabba5SLogan Gunthorpe if (!(ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR)) 103452eabba5SLogan Gunthorpe hdr &= ~SWITCHTEC_EVENT_CLEAR; 103552eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL) 103652eabba5SLogan Gunthorpe hdr |= SWITCHTEC_EVENT_EN_IRQ; 103752eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL) 103852eabba5SLogan Gunthorpe hdr &= ~SWITCHTEC_EVENT_EN_IRQ; 103952eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG) 104052eabba5SLogan Gunthorpe hdr |= SWITCHTEC_EVENT_EN_LOG; 104152eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG) 104252eabba5SLogan Gunthorpe hdr &= ~SWITCHTEC_EVENT_EN_LOG; 104352eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI) 104452eabba5SLogan Gunthorpe hdr |= SWITCHTEC_EVENT_EN_CLI; 104552eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI) 104652eabba5SLogan Gunthorpe hdr &= ~SWITCHTEC_EVENT_EN_CLI; 104752eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL) 104852eabba5SLogan Gunthorpe hdr |= SWITCHTEC_EVENT_FATAL; 104952eabba5SLogan Gunthorpe if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL) 105052eabba5SLogan Gunthorpe hdr &= ~SWITCHTEC_EVENT_FATAL; 105152eabba5SLogan Gunthorpe 105252eabba5SLogan Gunthorpe if (ctl->flags) 105352eabba5SLogan Gunthorpe iowrite32(hdr, reg); 105452eabba5SLogan Gunthorpe 105552eabba5SLogan Gunthorpe ctl->flags = 0; 105652eabba5SLogan Gunthorpe if (hdr & SWITCHTEC_EVENT_EN_IRQ) 105752eabba5SLogan Gunthorpe ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL; 105852eabba5SLogan Gunthorpe if (hdr & SWITCHTEC_EVENT_EN_LOG) 105952eabba5SLogan Gunthorpe ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG; 106052eabba5SLogan Gunthorpe if (hdr & SWITCHTEC_EVENT_EN_CLI) 106152eabba5SLogan Gunthorpe ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI; 106252eabba5SLogan Gunthorpe if (hdr & SWITCHTEC_EVENT_FATAL) 106352eabba5SLogan Gunthorpe ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL; 106452eabba5SLogan Gunthorpe 106552eabba5SLogan Gunthorpe return 0; 106652eabba5SLogan Gunthorpe } 106752eabba5SLogan Gunthorpe 106852eabba5SLogan Gunthorpe static int ioctl_event_ctl(struct switchtec_dev *stdev, 106952eabba5SLogan Gunthorpe struct switchtec_ioctl_event_ctl __user *uctl) 107052eabba5SLogan Gunthorpe { 107152eabba5SLogan Gunthorpe int ret; 107252eabba5SLogan Gunthorpe int nr_idxs; 1073e4a7dca5SJoey Zhang unsigned int event_flags; 107452eabba5SLogan Gunthorpe struct switchtec_ioctl_event_ctl ctl; 107552eabba5SLogan Gunthorpe 107652eabba5SLogan Gunthorpe if (copy_from_user(&ctl, uctl, sizeof(ctl))) 107752eabba5SLogan Gunthorpe return -EFAULT; 107852eabba5SLogan Gunthorpe 107952eabba5SLogan Gunthorpe if (ctl.event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) 108052eabba5SLogan Gunthorpe return -EINVAL; 108152eabba5SLogan Gunthorpe 108252eabba5SLogan Gunthorpe if (ctl.flags & SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED) 108352eabba5SLogan Gunthorpe return -EINVAL; 108452eabba5SLogan Gunthorpe 108552eabba5SLogan Gunthorpe if (ctl.index == SWITCHTEC_IOCTL_EVENT_IDX_ALL) { 108652eabba5SLogan Gunthorpe if (event_regs[ctl.event_id].map_reg == global_ev_reg) 108752eabba5SLogan Gunthorpe nr_idxs = 1; 108852eabba5SLogan Gunthorpe else if (event_regs[ctl.event_id].map_reg == part_ev_reg) 108952eabba5SLogan Gunthorpe nr_idxs = stdev->partition_count; 109052eabba5SLogan Gunthorpe else if (event_regs[ctl.event_id].map_reg == pff_ev_reg) 109152eabba5SLogan Gunthorpe nr_idxs = stdev->pff_csr_count; 109252eabba5SLogan Gunthorpe else 109352eabba5SLogan Gunthorpe return -EINVAL; 109452eabba5SLogan Gunthorpe 1095e4a7dca5SJoey Zhang event_flags = ctl.flags; 109652eabba5SLogan Gunthorpe for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { 1097e4a7dca5SJoey Zhang ctl.flags = event_flags; 109852eabba5SLogan Gunthorpe ret = event_ctl(stdev, &ctl); 109952eabba5SLogan Gunthorpe if (ret < 0) 110052eabba5SLogan Gunthorpe return ret; 110152eabba5SLogan Gunthorpe } 110252eabba5SLogan Gunthorpe } else { 110352eabba5SLogan Gunthorpe ret = event_ctl(stdev, &ctl); 110452eabba5SLogan Gunthorpe if (ret < 0) 110552eabba5SLogan Gunthorpe return ret; 110652eabba5SLogan Gunthorpe } 110752eabba5SLogan Gunthorpe 110852eabba5SLogan Gunthorpe if (copy_to_user(uctl, &ctl, sizeof(ctl))) 110952eabba5SLogan Gunthorpe return -EFAULT; 111052eabba5SLogan Gunthorpe 111152eabba5SLogan Gunthorpe return 0; 111252eabba5SLogan Gunthorpe } 111352eabba5SLogan Gunthorpe 111452eabba5SLogan Gunthorpe static int ioctl_pff_to_port(struct switchtec_dev *stdev, 11155f11723bSLogan Gunthorpe struct switchtec_ioctl_pff_port __user *up) 111652eabba5SLogan Gunthorpe { 111752eabba5SLogan Gunthorpe int i, part; 111852eabba5SLogan Gunthorpe u32 reg; 11195f11723bSLogan Gunthorpe struct part_cfg_regs __iomem *pcfg; 112052eabba5SLogan Gunthorpe struct switchtec_ioctl_pff_port p; 112152eabba5SLogan Gunthorpe 112252eabba5SLogan Gunthorpe if (copy_from_user(&p, up, sizeof(p))) 112352eabba5SLogan Gunthorpe return -EFAULT; 112452eabba5SLogan Gunthorpe 112552eabba5SLogan Gunthorpe p.port = -1; 112652eabba5SLogan Gunthorpe for (part = 0; part < stdev->partition_count; part++) { 112752eabba5SLogan Gunthorpe pcfg = &stdev->mmio_part_cfg_all[part]; 112852eabba5SLogan Gunthorpe p.partition = part; 112952eabba5SLogan Gunthorpe 113052eabba5SLogan Gunthorpe reg = ioread32(&pcfg->usp_pff_inst_id); 113152eabba5SLogan Gunthorpe if (reg == p.pff) { 113252eabba5SLogan Gunthorpe p.port = 0; 113352eabba5SLogan Gunthorpe break; 113452eabba5SLogan Gunthorpe } 113552eabba5SLogan Gunthorpe 113652eabba5SLogan Gunthorpe reg = ioread32(&pcfg->vep_pff_inst_id); 113752eabba5SLogan Gunthorpe if (reg == p.pff) { 113852eabba5SLogan Gunthorpe p.port = SWITCHTEC_IOCTL_PFF_VEP; 113952eabba5SLogan Gunthorpe break; 114052eabba5SLogan Gunthorpe } 114152eabba5SLogan Gunthorpe 114252eabba5SLogan Gunthorpe for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { 114352eabba5SLogan Gunthorpe reg = ioread32(&pcfg->dsp_pff_inst_id[i]); 114452eabba5SLogan Gunthorpe if (reg != p.pff) 114552eabba5SLogan Gunthorpe continue; 114652eabba5SLogan Gunthorpe 114752eabba5SLogan Gunthorpe p.port = i + 1; 114852eabba5SLogan Gunthorpe break; 114952eabba5SLogan Gunthorpe } 115052eabba5SLogan Gunthorpe 115152eabba5SLogan Gunthorpe if (p.port != -1) 115252eabba5SLogan Gunthorpe break; 115352eabba5SLogan Gunthorpe } 115452eabba5SLogan Gunthorpe 115552eabba5SLogan Gunthorpe if (copy_to_user(up, &p, sizeof(p))) 115652eabba5SLogan Gunthorpe return -EFAULT; 115752eabba5SLogan Gunthorpe 115852eabba5SLogan Gunthorpe return 0; 115952eabba5SLogan Gunthorpe } 116052eabba5SLogan Gunthorpe 116152eabba5SLogan Gunthorpe static int ioctl_port_to_pff(struct switchtec_dev *stdev, 11625f11723bSLogan Gunthorpe struct switchtec_ioctl_pff_port __user *up) 116352eabba5SLogan Gunthorpe { 116452eabba5SLogan Gunthorpe struct switchtec_ioctl_pff_port p; 11655f11723bSLogan Gunthorpe struct part_cfg_regs __iomem *pcfg; 116652eabba5SLogan Gunthorpe 116752eabba5SLogan Gunthorpe if (copy_from_user(&p, up, sizeof(p))) 116852eabba5SLogan Gunthorpe return -EFAULT; 116952eabba5SLogan Gunthorpe 117052eabba5SLogan Gunthorpe if (p.partition == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) 117152eabba5SLogan Gunthorpe pcfg = stdev->mmio_part_cfg; 117252eabba5SLogan Gunthorpe else if (p.partition < stdev->partition_count) 117352eabba5SLogan Gunthorpe pcfg = &stdev->mmio_part_cfg_all[p.partition]; 117452eabba5SLogan Gunthorpe else 117552eabba5SLogan Gunthorpe return -EINVAL; 117652eabba5SLogan Gunthorpe 117752eabba5SLogan Gunthorpe switch (p.port) { 117852eabba5SLogan Gunthorpe case 0: 117952eabba5SLogan Gunthorpe p.pff = ioread32(&pcfg->usp_pff_inst_id); 118052eabba5SLogan Gunthorpe break; 118152eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PFF_VEP: 118252eabba5SLogan Gunthorpe p.pff = ioread32(&pcfg->vep_pff_inst_id); 118352eabba5SLogan Gunthorpe break; 118452eabba5SLogan Gunthorpe default: 118552eabba5SLogan Gunthorpe if (p.port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) 118652eabba5SLogan Gunthorpe return -EINVAL; 118746feb6b4SGustavo A. R. Silva p.port = array_index_nospec(p.port, 118846feb6b4SGustavo A. R. Silva ARRAY_SIZE(pcfg->dsp_pff_inst_id) + 1); 118952eabba5SLogan Gunthorpe p.pff = ioread32(&pcfg->dsp_pff_inst_id[p.port - 1]); 119052eabba5SLogan Gunthorpe break; 119152eabba5SLogan Gunthorpe } 119252eabba5SLogan Gunthorpe 119352eabba5SLogan Gunthorpe if (copy_to_user(up, &p, sizeof(p))) 119452eabba5SLogan Gunthorpe return -EFAULT; 119552eabba5SLogan Gunthorpe 119652eabba5SLogan Gunthorpe return 0; 119752eabba5SLogan Gunthorpe } 119852eabba5SLogan Gunthorpe 119952eabba5SLogan Gunthorpe static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd, 120052eabba5SLogan Gunthorpe unsigned long arg) 120152eabba5SLogan Gunthorpe { 120252eabba5SLogan Gunthorpe struct switchtec_user *stuser = filp->private_data; 120352eabba5SLogan Gunthorpe struct switchtec_dev *stdev = stuser->stdev; 120452eabba5SLogan Gunthorpe int rc; 120552eabba5SLogan Gunthorpe void __user *argp = (void __user *)arg; 120652eabba5SLogan Gunthorpe 120752eabba5SLogan Gunthorpe rc = lock_mutex_and_test_alive(stdev); 120852eabba5SLogan Gunthorpe if (rc) 120952eabba5SLogan Gunthorpe return rc; 121052eabba5SLogan Gunthorpe 121152eabba5SLogan Gunthorpe switch (cmd) { 121252eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_FLASH_INFO: 121352eabba5SLogan Gunthorpe rc = ioctl_flash_info(stdev, argp); 121452eabba5SLogan Gunthorpe break; 121552eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_FLASH_PART_INFO: 121652eabba5SLogan Gunthorpe rc = ioctl_flash_part_info(stdev, argp); 121752eabba5SLogan Gunthorpe break; 1218ba8a3982SWesley Sheng case SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY: 1219ba8a3982SWesley Sheng rc = ioctl_event_summary(stdev, stuser, argp, 1220ba8a3982SWesley Sheng sizeof(struct switchtec_ioctl_event_summary_legacy)); 122152eabba5SLogan Gunthorpe break; 122252eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_EVENT_CTL: 122352eabba5SLogan Gunthorpe rc = ioctl_event_ctl(stdev, argp); 122452eabba5SLogan Gunthorpe break; 122552eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PFF_TO_PORT: 122652eabba5SLogan Gunthorpe rc = ioctl_pff_to_port(stdev, argp); 122752eabba5SLogan Gunthorpe break; 122852eabba5SLogan Gunthorpe case SWITCHTEC_IOCTL_PORT_TO_PFF: 122952eabba5SLogan Gunthorpe rc = ioctl_port_to_pff(stdev, argp); 123052eabba5SLogan Gunthorpe break; 1231ba8a3982SWesley Sheng case SWITCHTEC_IOCTL_EVENT_SUMMARY: 1232ba8a3982SWesley Sheng rc = ioctl_event_summary(stdev, stuser, argp, 1233ba8a3982SWesley Sheng sizeof(struct switchtec_ioctl_event_summary)); 1234ba8a3982SWesley Sheng break; 123552eabba5SLogan Gunthorpe default: 123652eabba5SLogan Gunthorpe rc = -ENOTTY; 123752eabba5SLogan Gunthorpe break; 123852eabba5SLogan Gunthorpe } 123952eabba5SLogan Gunthorpe 124052eabba5SLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 124152eabba5SLogan Gunthorpe return rc; 124252eabba5SLogan Gunthorpe } 124352eabba5SLogan Gunthorpe 1244080b47deSLogan Gunthorpe static const struct file_operations switchtec_fops = { 1245080b47deSLogan Gunthorpe .owner = THIS_MODULE, 1246080b47deSLogan Gunthorpe .open = switchtec_dev_open, 1247080b47deSLogan Gunthorpe .release = switchtec_dev_release, 1248080b47deSLogan Gunthorpe .write = switchtec_dev_write, 1249080b47deSLogan Gunthorpe .read = switchtec_dev_read, 1250080b47deSLogan Gunthorpe .poll = switchtec_dev_poll, 125152eabba5SLogan Gunthorpe .unlocked_ioctl = switchtec_dev_ioctl, 12521832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl, 1253080b47deSLogan Gunthorpe }; 1254080b47deSLogan Gunthorpe 125548c302dcSLogan Gunthorpe static void link_event_work(struct work_struct *work) 125648c302dcSLogan Gunthorpe { 125748c302dcSLogan Gunthorpe struct switchtec_dev *stdev; 125848c302dcSLogan Gunthorpe 125948c302dcSLogan Gunthorpe stdev = container_of(work, struct switchtec_dev, link_event_work); 126048c302dcSLogan Gunthorpe 126148c302dcSLogan Gunthorpe if (stdev->link_notifier) 126248c302dcSLogan Gunthorpe stdev->link_notifier(stdev); 126348c302dcSLogan Gunthorpe } 126448c302dcSLogan Gunthorpe 126548c302dcSLogan Gunthorpe static void check_link_state_events(struct switchtec_dev *stdev) 126648c302dcSLogan Gunthorpe { 126748c302dcSLogan Gunthorpe int idx; 126848c302dcSLogan Gunthorpe u32 reg; 126948c302dcSLogan Gunthorpe int count; 127048c302dcSLogan Gunthorpe int occurred = 0; 127148c302dcSLogan Gunthorpe 127248c302dcSLogan Gunthorpe for (idx = 0; idx < stdev->pff_csr_count; idx++) { 127348c302dcSLogan Gunthorpe reg = ioread32(&stdev->mmio_pff_csr[idx].link_state_hdr); 127448c302dcSLogan Gunthorpe dev_dbg(&stdev->dev, "link_state: %d->%08x\n", idx, reg); 127548c302dcSLogan Gunthorpe count = (reg >> 5) & 0xFF; 127648c302dcSLogan Gunthorpe 127748c302dcSLogan Gunthorpe if (count != stdev->link_event_count[idx]) { 127848c302dcSLogan Gunthorpe occurred = 1; 127948c302dcSLogan Gunthorpe stdev->link_event_count[idx] = count; 128048c302dcSLogan Gunthorpe } 128148c302dcSLogan Gunthorpe } 128248c302dcSLogan Gunthorpe 128348c302dcSLogan Gunthorpe if (occurred) 128448c302dcSLogan Gunthorpe schedule_work(&stdev->link_event_work); 128548c302dcSLogan Gunthorpe } 128648c302dcSLogan Gunthorpe 128748c302dcSLogan Gunthorpe static void enable_link_state_events(struct switchtec_dev *stdev) 128848c302dcSLogan Gunthorpe { 128948c302dcSLogan Gunthorpe int idx; 129048c302dcSLogan Gunthorpe 129148c302dcSLogan Gunthorpe for (idx = 0; idx < stdev->pff_csr_count; idx++) { 129248c302dcSLogan Gunthorpe iowrite32(SWITCHTEC_EVENT_CLEAR | 129348c302dcSLogan Gunthorpe SWITCHTEC_EVENT_EN_IRQ, 129448c302dcSLogan Gunthorpe &stdev->mmio_pff_csr[idx].link_state_hdr); 129548c302dcSLogan Gunthorpe } 129648c302dcSLogan Gunthorpe } 129748c302dcSLogan Gunthorpe 1298f7eb7b8aSWesley Sheng static void enable_dma_mrpc(struct switchtec_dev *stdev) 1299f7eb7b8aSWesley Sheng { 1300f7eb7b8aSWesley Sheng writeq(stdev->dma_mrpc_dma_addr, &stdev->mmio_mrpc->dma_addr); 1301f7eb7b8aSWesley Sheng flush_wc_buf(stdev); 1302f7eb7b8aSWesley Sheng iowrite32(SWITCHTEC_DMA_MRPC_EN, &stdev->mmio_mrpc->dma_en); 1303f7eb7b8aSWesley Sheng } 1304f7eb7b8aSWesley Sheng 1305080b47deSLogan Gunthorpe static void stdev_release(struct device *dev) 1306080b47deSLogan Gunthorpe { 1307080b47deSLogan Gunthorpe struct switchtec_dev *stdev = to_stdev(dev); 1308080b47deSLogan Gunthorpe 1309f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) { 1310f7eb7b8aSWesley Sheng iowrite32(0, &stdev->mmio_mrpc->dma_en); 1311f7eb7b8aSWesley Sheng flush_wc_buf(stdev); 1312f7eb7b8aSWesley Sheng writeq(0, &stdev->mmio_mrpc->dma_addr); 1313f7eb7b8aSWesley Sheng dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), 1314f7eb7b8aSWesley Sheng stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); 1315f7eb7b8aSWesley Sheng } 1316080b47deSLogan Gunthorpe kfree(stdev); 1317080b47deSLogan Gunthorpe } 1318080b47deSLogan Gunthorpe 1319080b47deSLogan Gunthorpe static void stdev_kill(struct switchtec_dev *stdev) 1320080b47deSLogan Gunthorpe { 1321080b47deSLogan Gunthorpe struct switchtec_user *stuser, *tmpuser; 1322080b47deSLogan Gunthorpe 1323080b47deSLogan Gunthorpe pci_clear_master(stdev->pdev); 1324080b47deSLogan Gunthorpe 1325080b47deSLogan Gunthorpe cancel_delayed_work_sync(&stdev->mrpc_timeout); 1326080b47deSLogan Gunthorpe 1327080b47deSLogan Gunthorpe /* Mark the hardware as unavailable and complete all completions */ 1328080b47deSLogan Gunthorpe mutex_lock(&stdev->mrpc_mutex); 1329080b47deSLogan Gunthorpe stdev->alive = false; 1330080b47deSLogan Gunthorpe 1331080b47deSLogan Gunthorpe /* Wake up and kill any users waiting on an MRPC request */ 1332080b47deSLogan Gunthorpe list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) { 1333deaa0a8aSSebastian Andrzej Siewior stuser->cmd_done = true; 1334deaa0a8aSSebastian Andrzej Siewior wake_up_interruptible(&stuser->cmd_comp); 1335080b47deSLogan Gunthorpe list_del_init(&stuser->list); 1336080b47deSLogan Gunthorpe stuser_put(stuser); 1337080b47deSLogan Gunthorpe } 1338080b47deSLogan Gunthorpe 1339080b47deSLogan Gunthorpe mutex_unlock(&stdev->mrpc_mutex); 1340080b47deSLogan Gunthorpe 1341080b47deSLogan Gunthorpe /* Wake up any users waiting on event_wq */ 1342080b47deSLogan Gunthorpe wake_up_interruptible(&stdev->event_wq); 1343080b47deSLogan Gunthorpe } 1344080b47deSLogan Gunthorpe 1345080b47deSLogan Gunthorpe static struct switchtec_dev *stdev_create(struct pci_dev *pdev) 1346080b47deSLogan Gunthorpe { 1347080b47deSLogan Gunthorpe struct switchtec_dev *stdev; 1348080b47deSLogan Gunthorpe int minor; 1349080b47deSLogan Gunthorpe struct device *dev; 1350080b47deSLogan Gunthorpe struct cdev *cdev; 1351080b47deSLogan Gunthorpe int rc; 1352080b47deSLogan Gunthorpe 1353080b47deSLogan Gunthorpe stdev = kzalloc_node(sizeof(*stdev), GFP_KERNEL, 1354080b47deSLogan Gunthorpe dev_to_node(&pdev->dev)); 1355080b47deSLogan Gunthorpe if (!stdev) 1356080b47deSLogan Gunthorpe return ERR_PTR(-ENOMEM); 1357080b47deSLogan Gunthorpe 1358080b47deSLogan Gunthorpe stdev->alive = true; 1359080b47deSLogan Gunthorpe stdev->pdev = pdev; 1360080b47deSLogan Gunthorpe INIT_LIST_HEAD(&stdev->mrpc_queue); 1361080b47deSLogan Gunthorpe mutex_init(&stdev->mrpc_mutex); 1362080b47deSLogan Gunthorpe stdev->mrpc_busy = 0; 1363080b47deSLogan Gunthorpe INIT_WORK(&stdev->mrpc_work, mrpc_event_work); 1364080b47deSLogan Gunthorpe INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work); 136548c302dcSLogan Gunthorpe INIT_WORK(&stdev->link_event_work, link_event_work); 1366080b47deSLogan Gunthorpe init_waitqueue_head(&stdev->event_wq); 1367080b47deSLogan Gunthorpe atomic_set(&stdev->event_cnt, 0); 1368080b47deSLogan Gunthorpe 1369080b47deSLogan Gunthorpe dev = &stdev->dev; 1370080b47deSLogan Gunthorpe device_initialize(dev); 1371080b47deSLogan Gunthorpe dev->class = switchtec_class; 1372080b47deSLogan Gunthorpe dev->parent = &pdev->dev; 13735d8e1881SLogan Gunthorpe dev->groups = switchtec_device_groups; 1374080b47deSLogan Gunthorpe dev->release = stdev_release; 1375080b47deSLogan Gunthorpe 1376080b47deSLogan Gunthorpe minor = ida_simple_get(&switchtec_minor_ida, 0, 0, 1377080b47deSLogan Gunthorpe GFP_KERNEL); 1378080b47deSLogan Gunthorpe if (minor < 0) { 1379080b47deSLogan Gunthorpe rc = minor; 1380080b47deSLogan Gunthorpe goto err_put; 1381080b47deSLogan Gunthorpe } 1382080b47deSLogan Gunthorpe 1383080b47deSLogan Gunthorpe dev->devt = MKDEV(MAJOR(switchtec_devt), minor); 1384080b47deSLogan Gunthorpe dev_set_name(dev, "switchtec%d", minor); 1385080b47deSLogan Gunthorpe 1386080b47deSLogan Gunthorpe cdev = &stdev->cdev; 1387080b47deSLogan Gunthorpe cdev_init(cdev, &switchtec_fops); 1388080b47deSLogan Gunthorpe cdev->owner = THIS_MODULE; 1389080b47deSLogan Gunthorpe 1390080b47deSLogan Gunthorpe return stdev; 1391080b47deSLogan Gunthorpe 1392080b47deSLogan Gunthorpe err_put: 1393080b47deSLogan Gunthorpe put_device(&stdev->dev); 1394080b47deSLogan Gunthorpe return ERR_PTR(rc); 1395080b47deSLogan Gunthorpe } 1396080b47deSLogan Gunthorpe 139752eabba5SLogan Gunthorpe static int mask_event(struct switchtec_dev *stdev, int eid, int idx) 139852eabba5SLogan Gunthorpe { 139952eabba5SLogan Gunthorpe size_t off = event_regs[eid].offset; 140052eabba5SLogan Gunthorpe u32 __iomem *hdr_reg; 140152eabba5SLogan Gunthorpe u32 hdr; 140252eabba5SLogan Gunthorpe 140352eabba5SLogan Gunthorpe hdr_reg = event_regs[eid].map_reg(stdev, off, idx); 140452eabba5SLogan Gunthorpe hdr = ioread32(hdr_reg); 140552eabba5SLogan Gunthorpe 140652eabba5SLogan Gunthorpe if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) 140752eabba5SLogan Gunthorpe return 0; 140852eabba5SLogan Gunthorpe 140952eabba5SLogan Gunthorpe dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); 141052eabba5SLogan Gunthorpe hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); 141152eabba5SLogan Gunthorpe iowrite32(hdr, hdr_reg); 141252eabba5SLogan Gunthorpe 141352eabba5SLogan Gunthorpe return 1; 141452eabba5SLogan Gunthorpe } 141552eabba5SLogan Gunthorpe 141652eabba5SLogan Gunthorpe static int mask_all_events(struct switchtec_dev *stdev, int eid) 141752eabba5SLogan Gunthorpe { 141852eabba5SLogan Gunthorpe int idx; 141952eabba5SLogan Gunthorpe int count = 0; 142052eabba5SLogan Gunthorpe 142152eabba5SLogan Gunthorpe if (event_regs[eid].map_reg == part_ev_reg) { 142252eabba5SLogan Gunthorpe for (idx = 0; idx < stdev->partition_count; idx++) 142352eabba5SLogan Gunthorpe count += mask_event(stdev, eid, idx); 142452eabba5SLogan Gunthorpe } else if (event_regs[eid].map_reg == pff_ev_reg) { 142552eabba5SLogan Gunthorpe for (idx = 0; idx < stdev->pff_csr_count; idx++) { 142652eabba5SLogan Gunthorpe if (!stdev->pff_local[idx]) 142752eabba5SLogan Gunthorpe continue; 142848c302dcSLogan Gunthorpe 142952eabba5SLogan Gunthorpe count += mask_event(stdev, eid, idx); 143052eabba5SLogan Gunthorpe } 143152eabba5SLogan Gunthorpe } else { 143252eabba5SLogan Gunthorpe count += mask_event(stdev, eid, 0); 143352eabba5SLogan Gunthorpe } 143452eabba5SLogan Gunthorpe 143552eabba5SLogan Gunthorpe return count; 143652eabba5SLogan Gunthorpe } 143752eabba5SLogan Gunthorpe 1438080b47deSLogan Gunthorpe static irqreturn_t switchtec_event_isr(int irq, void *dev) 1439080b47deSLogan Gunthorpe { 1440080b47deSLogan Gunthorpe struct switchtec_dev *stdev = dev; 1441080b47deSLogan Gunthorpe u32 reg; 1442080b47deSLogan Gunthorpe irqreturn_t ret = IRQ_NONE; 144352eabba5SLogan Gunthorpe int eid, event_count = 0; 1444080b47deSLogan Gunthorpe 1445080b47deSLogan Gunthorpe reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr); 1446080b47deSLogan Gunthorpe if (reg & SWITCHTEC_EVENT_OCCURRED) { 1447080b47deSLogan Gunthorpe dev_dbg(&stdev->dev, "%s: mrpc comp\n", __func__); 1448080b47deSLogan Gunthorpe ret = IRQ_HANDLED; 1449080b47deSLogan Gunthorpe schedule_work(&stdev->mrpc_work); 1450080b47deSLogan Gunthorpe iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); 1451080b47deSLogan Gunthorpe } 1452080b47deSLogan Gunthorpe 145348c302dcSLogan Gunthorpe check_link_state_events(stdev); 145448c302dcSLogan Gunthorpe 14552085747dSWesley Sheng for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) { 14562085747dSWesley Sheng if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE || 14572085747dSWesley Sheng eid == SWITCHTEC_IOCTL_EVENT_MRPC_COMP) 14582085747dSWesley Sheng continue; 14592085747dSWesley Sheng 146052eabba5SLogan Gunthorpe event_count += mask_all_events(stdev, eid); 14612085747dSWesley Sheng } 146252eabba5SLogan Gunthorpe 146352eabba5SLogan Gunthorpe if (event_count) { 146452eabba5SLogan Gunthorpe atomic_inc(&stdev->event_cnt); 146552eabba5SLogan Gunthorpe wake_up_interruptible(&stdev->event_wq); 146652eabba5SLogan Gunthorpe dev_dbg(&stdev->dev, "%s: %d events\n", __func__, 146752eabba5SLogan Gunthorpe event_count); 146852eabba5SLogan Gunthorpe return IRQ_HANDLED; 146952eabba5SLogan Gunthorpe } 147052eabba5SLogan Gunthorpe 1471080b47deSLogan Gunthorpe return ret; 1472080b47deSLogan Gunthorpe } 1473080b47deSLogan Gunthorpe 1474f7eb7b8aSWesley Sheng 1475f7eb7b8aSWesley Sheng static irqreturn_t switchtec_dma_mrpc_isr(int irq, void *dev) 1476f7eb7b8aSWesley Sheng { 1477f7eb7b8aSWesley Sheng struct switchtec_dev *stdev = dev; 1478f7eb7b8aSWesley Sheng irqreturn_t ret = IRQ_NONE; 1479f7eb7b8aSWesley Sheng 1480f7eb7b8aSWesley Sheng iowrite32(SWITCHTEC_EVENT_CLEAR | 1481f7eb7b8aSWesley Sheng SWITCHTEC_EVENT_EN_IRQ, 1482f7eb7b8aSWesley Sheng &stdev->mmio_part_cfg->mrpc_comp_hdr); 1483f7eb7b8aSWesley Sheng schedule_work(&stdev->mrpc_work); 1484f7eb7b8aSWesley Sheng 1485f7eb7b8aSWesley Sheng ret = IRQ_HANDLED; 1486f7eb7b8aSWesley Sheng return ret; 1487f7eb7b8aSWesley Sheng } 1488f7eb7b8aSWesley Sheng 1489080b47deSLogan Gunthorpe static int switchtec_init_isr(struct switchtec_dev *stdev) 1490080b47deSLogan Gunthorpe { 1491080b47deSLogan Gunthorpe int nvecs; 1492080b47deSLogan Gunthorpe int event_irq; 1493f7eb7b8aSWesley Sheng int dma_mrpc_irq; 1494f7eb7b8aSWesley Sheng int rc; 1495080b47deSLogan Gunthorpe 1496fcdf8e95SLogan Gunthorpe if (nirqs < 4) 1497fcdf8e95SLogan Gunthorpe nirqs = 4; 1498fcdf8e95SLogan Gunthorpe 1499fcdf8e95SLogan Gunthorpe nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, nirqs, 1500fcdf8e95SLogan Gunthorpe PCI_IRQ_MSIX | PCI_IRQ_MSI | 1501fcdf8e95SLogan Gunthorpe PCI_IRQ_VIRTUAL); 1502080b47deSLogan Gunthorpe if (nvecs < 0) 1503080b47deSLogan Gunthorpe return nvecs; 1504080b47deSLogan Gunthorpe 15059375646bSLogan Gunthorpe event_irq = ioread16(&stdev->mmio_part_cfg->vep_vector_number); 1506080b47deSLogan Gunthorpe if (event_irq < 0 || event_irq >= nvecs) 1507080b47deSLogan Gunthorpe return -EFAULT; 1508080b47deSLogan Gunthorpe 1509080b47deSLogan Gunthorpe event_irq = pci_irq_vector(stdev->pdev, event_irq); 1510080b47deSLogan Gunthorpe if (event_irq < 0) 1511080b47deSLogan Gunthorpe return event_irq; 1512080b47deSLogan Gunthorpe 1513f7eb7b8aSWesley Sheng rc = devm_request_irq(&stdev->pdev->dev, event_irq, 1514080b47deSLogan Gunthorpe switchtec_event_isr, 0, 1515080b47deSLogan Gunthorpe KBUILD_MODNAME, stdev); 1516f7eb7b8aSWesley Sheng 1517f7eb7b8aSWesley Sheng if (rc) 1518f7eb7b8aSWesley Sheng return rc; 1519f7eb7b8aSWesley Sheng 1520f7eb7b8aSWesley Sheng if (!stdev->dma_mrpc) 1521f7eb7b8aSWesley Sheng return rc; 1522f7eb7b8aSWesley Sheng 1523f7eb7b8aSWesley Sheng dma_mrpc_irq = ioread32(&stdev->mmio_mrpc->dma_vector); 1524f7eb7b8aSWesley Sheng if (dma_mrpc_irq < 0 || dma_mrpc_irq >= nvecs) 1525f7eb7b8aSWesley Sheng return -EFAULT; 1526f7eb7b8aSWesley Sheng 1527f7eb7b8aSWesley Sheng dma_mrpc_irq = pci_irq_vector(stdev->pdev, dma_mrpc_irq); 1528f7eb7b8aSWesley Sheng if (dma_mrpc_irq < 0) 1529f7eb7b8aSWesley Sheng return dma_mrpc_irq; 1530f7eb7b8aSWesley Sheng 1531f7eb7b8aSWesley Sheng rc = devm_request_irq(&stdev->pdev->dev, dma_mrpc_irq, 1532f7eb7b8aSWesley Sheng switchtec_dma_mrpc_isr, 0, 1533f7eb7b8aSWesley Sheng KBUILD_MODNAME, stdev); 1534f7eb7b8aSWesley Sheng 1535f7eb7b8aSWesley Sheng return rc; 1536080b47deSLogan Gunthorpe } 1537080b47deSLogan Gunthorpe 1538080b47deSLogan Gunthorpe static void init_pff(struct switchtec_dev *stdev) 1539080b47deSLogan Gunthorpe { 1540080b47deSLogan Gunthorpe int i; 1541080b47deSLogan Gunthorpe u32 reg; 154242dae893SLogan Gunthorpe struct part_cfg_regs __iomem *pcfg = stdev->mmio_part_cfg; 1543080b47deSLogan Gunthorpe 1544080b47deSLogan Gunthorpe for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { 1545080b47deSLogan Gunthorpe reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); 1546cfdfc14eSDoug Meyer if (reg != PCI_VENDOR_ID_MICROSEMI) 1547080b47deSLogan Gunthorpe break; 1548080b47deSLogan Gunthorpe } 1549080b47deSLogan Gunthorpe 1550080b47deSLogan Gunthorpe stdev->pff_csr_count = i; 1551080b47deSLogan Gunthorpe 1552080b47deSLogan Gunthorpe reg = ioread32(&pcfg->usp_pff_inst_id); 15537501a02aSWesley Sheng if (reg < stdev->pff_csr_count) 1554080b47deSLogan Gunthorpe stdev->pff_local[reg] = 1; 1555080b47deSLogan Gunthorpe 1556080b47deSLogan Gunthorpe reg = ioread32(&pcfg->vep_pff_inst_id); 15577501a02aSWesley Sheng if (reg < stdev->pff_csr_count) 1558080b47deSLogan Gunthorpe stdev->pff_local[reg] = 1; 1559080b47deSLogan Gunthorpe 1560080b47deSLogan Gunthorpe for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { 1561080b47deSLogan Gunthorpe reg = ioread32(&pcfg->dsp_pff_inst_id[i]); 15627501a02aSWesley Sheng if (reg < stdev->pff_csr_count) 1563080b47deSLogan Gunthorpe stdev->pff_local[reg] = 1; 1564080b47deSLogan Gunthorpe } 1565080b47deSLogan Gunthorpe } 1566080b47deSLogan Gunthorpe 1567080b47deSLogan Gunthorpe static int switchtec_init_pci(struct switchtec_dev *stdev, 1568080b47deSLogan Gunthorpe struct pci_dev *pdev) 1569080b47deSLogan Gunthorpe { 1570080b47deSLogan Gunthorpe int rc; 157152d8db8eSKelvin Cao void __iomem *map; 157252d8db8eSKelvin Cao unsigned long res_start, res_len; 1573993d208dSLogan Gunthorpe u32 __iomem *part_id; 1574080b47deSLogan Gunthorpe 1575080b47deSLogan Gunthorpe rc = pcim_enable_device(pdev); 1576080b47deSLogan Gunthorpe if (rc) 1577080b47deSLogan Gunthorpe return rc; 1578080b47deSLogan Gunthorpe 1579aa82130aSWesley Sheng rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 1580aff614c6SBoris Glimcher if (rc) 1581aff614c6SBoris Glimcher return rc; 1582aff614c6SBoris Glimcher 1583080b47deSLogan Gunthorpe pci_set_master(pdev); 1584080b47deSLogan Gunthorpe 158552d8db8eSKelvin Cao res_start = pci_resource_start(pdev, 0); 158652d8db8eSKelvin Cao res_len = pci_resource_len(pdev, 0); 158752d8db8eSKelvin Cao 158852d8db8eSKelvin Cao if (!devm_request_mem_region(&pdev->dev, res_start, 158952d8db8eSKelvin Cao res_len, KBUILD_MODNAME)) 159052d8db8eSKelvin Cao return -EBUSY; 159152d8db8eSKelvin Cao 159252d8db8eSKelvin Cao stdev->mmio_mrpc = devm_ioremap_wc(&pdev->dev, res_start, 159352d8db8eSKelvin Cao SWITCHTEC_GAS_TOP_CFG_OFFSET); 159452d8db8eSKelvin Cao if (!stdev->mmio_mrpc) 159552d8db8eSKelvin Cao return -ENOMEM; 159652d8db8eSKelvin Cao 159752d8db8eSKelvin Cao map = devm_ioremap(&pdev->dev, 159852d8db8eSKelvin Cao res_start + SWITCHTEC_GAS_TOP_CFG_OFFSET, 159952d8db8eSKelvin Cao res_len - SWITCHTEC_GAS_TOP_CFG_OFFSET); 160052d8db8eSKelvin Cao if (!map) 160152d8db8eSKelvin Cao return -ENOMEM; 160252d8db8eSKelvin Cao 160352d8db8eSKelvin Cao stdev->mmio = map - SWITCHTEC_GAS_TOP_CFG_OFFSET; 1604080b47deSLogan Gunthorpe stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET; 1605080b47deSLogan Gunthorpe stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; 1606080b47deSLogan Gunthorpe stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; 1607080b47deSLogan Gunthorpe stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET; 1608993d208dSLogan Gunthorpe 1609993d208dSLogan Gunthorpe if (stdev->gen == SWITCHTEC_GEN3) 1610993d208dSLogan Gunthorpe part_id = &stdev->mmio_sys_info->gen3.partition_id; 1611a3321ca3SLogan Gunthorpe else if (stdev->gen == SWITCHTEC_GEN4) 1612a3321ca3SLogan Gunthorpe part_id = &stdev->mmio_sys_info->gen4.partition_id; 1613993d208dSLogan Gunthorpe else 1614993d208dSLogan Gunthorpe return -ENOTSUPP; 1615993d208dSLogan Gunthorpe 1616993d208dSLogan Gunthorpe stdev->partition = ioread8(part_id); 1617080b47deSLogan Gunthorpe stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count); 1618080b47deSLogan Gunthorpe stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET; 1619080b47deSLogan Gunthorpe stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition]; 1620080b47deSLogan Gunthorpe stdev->mmio_pff_csr = stdev->mmio + SWITCHTEC_GAS_PFF_CSR_OFFSET; 1621080b47deSLogan Gunthorpe 16229871e9bbSLogan Gunthorpe if (stdev->partition_count < 1) 16239871e9bbSLogan Gunthorpe stdev->partition_count = 1; 16249871e9bbSLogan Gunthorpe 1625080b47deSLogan Gunthorpe init_pff(stdev); 1626080b47deSLogan Gunthorpe 1627080b47deSLogan Gunthorpe pci_set_drvdata(pdev, stdev); 1628080b47deSLogan Gunthorpe 1629f7eb7b8aSWesley Sheng if (!use_dma_mrpc) 1630f7eb7b8aSWesley Sheng return 0; 1631f7eb7b8aSWesley Sheng 1632f7eb7b8aSWesley Sheng if (ioread32(&stdev->mmio_mrpc->dma_ver) == 0) 1633f7eb7b8aSWesley Sheng return 0; 1634f7eb7b8aSWesley Sheng 1635750afb08SLuis Chamberlain stdev->dma_mrpc = dma_alloc_coherent(&stdev->pdev->dev, 1636f7eb7b8aSWesley Sheng sizeof(*stdev->dma_mrpc), 1637f7eb7b8aSWesley Sheng &stdev->dma_mrpc_dma_addr, 1638f7eb7b8aSWesley Sheng GFP_KERNEL); 1639f7eb7b8aSWesley Sheng if (stdev->dma_mrpc == NULL) 1640f7eb7b8aSWesley Sheng return -ENOMEM; 1641f7eb7b8aSWesley Sheng 1642080b47deSLogan Gunthorpe return 0; 1643080b47deSLogan Gunthorpe } 1644080b47deSLogan Gunthorpe 1645080b47deSLogan Gunthorpe static int switchtec_pci_probe(struct pci_dev *pdev, 1646080b47deSLogan Gunthorpe const struct pci_device_id *id) 1647080b47deSLogan Gunthorpe { 1648080b47deSLogan Gunthorpe struct switchtec_dev *stdev; 1649080b47deSLogan Gunthorpe int rc; 1650080b47deSLogan Gunthorpe 1651cfdfc14eSDoug Meyer if (pdev->class == (PCI_CLASS_BRIDGE_OTHER << 8)) 165233dea5aaSLogan Gunthorpe request_module_nowait("ntb_hw_switchtec"); 165333dea5aaSLogan Gunthorpe 1654080b47deSLogan Gunthorpe stdev = stdev_create(pdev); 1655080b47deSLogan Gunthorpe if (IS_ERR(stdev)) 1656080b47deSLogan Gunthorpe return PTR_ERR(stdev); 1657080b47deSLogan Gunthorpe 1658b13313a0SLogan Gunthorpe stdev->gen = id->driver_data; 1659b13313a0SLogan Gunthorpe 1660080b47deSLogan Gunthorpe rc = switchtec_init_pci(stdev, pdev); 1661080b47deSLogan Gunthorpe if (rc) 1662080b47deSLogan Gunthorpe goto err_put; 1663080b47deSLogan Gunthorpe 1664080b47deSLogan Gunthorpe rc = switchtec_init_isr(stdev); 1665080b47deSLogan Gunthorpe if (rc) { 1666080b47deSLogan Gunthorpe dev_err(&stdev->dev, "failed to init isr.\n"); 1667080b47deSLogan Gunthorpe goto err_put; 1668080b47deSLogan Gunthorpe } 1669080b47deSLogan Gunthorpe 1670080b47deSLogan Gunthorpe iowrite32(SWITCHTEC_EVENT_CLEAR | 1671080b47deSLogan Gunthorpe SWITCHTEC_EVENT_EN_IRQ, 1672080b47deSLogan Gunthorpe &stdev->mmio_part_cfg->mrpc_comp_hdr); 167348c302dcSLogan Gunthorpe enable_link_state_events(stdev); 1674080b47deSLogan Gunthorpe 1675f7eb7b8aSWesley Sheng if (stdev->dma_mrpc) 1676f7eb7b8aSWesley Sheng enable_dma_mrpc(stdev); 1677f7eb7b8aSWesley Sheng 1678e40cf640SLogan Gunthorpe rc = cdev_device_add(&stdev->cdev, &stdev->dev); 1679080b47deSLogan Gunthorpe if (rc) 1680080b47deSLogan Gunthorpe goto err_devadd; 1681080b47deSLogan Gunthorpe 1682080b47deSLogan Gunthorpe dev_info(&stdev->dev, "Management device registered.\n"); 1683080b47deSLogan Gunthorpe 1684080b47deSLogan Gunthorpe return 0; 1685080b47deSLogan Gunthorpe 1686080b47deSLogan Gunthorpe err_devadd: 1687080b47deSLogan Gunthorpe stdev_kill(stdev); 1688080b47deSLogan Gunthorpe err_put: 1689080b47deSLogan Gunthorpe ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); 1690080b47deSLogan Gunthorpe put_device(&stdev->dev); 1691080b47deSLogan Gunthorpe return rc; 1692080b47deSLogan Gunthorpe } 1693080b47deSLogan Gunthorpe 1694080b47deSLogan Gunthorpe static void switchtec_pci_remove(struct pci_dev *pdev) 1695080b47deSLogan Gunthorpe { 1696080b47deSLogan Gunthorpe struct switchtec_dev *stdev = pci_get_drvdata(pdev); 1697080b47deSLogan Gunthorpe 1698080b47deSLogan Gunthorpe pci_set_drvdata(pdev, NULL); 1699080b47deSLogan Gunthorpe 1700e40cf640SLogan Gunthorpe cdev_device_del(&stdev->cdev, &stdev->dev); 1701080b47deSLogan Gunthorpe ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); 1702080b47deSLogan Gunthorpe dev_info(&stdev->dev, "unregistered.\n"); 1703080b47deSLogan Gunthorpe stdev_kill(stdev); 1704080b47deSLogan Gunthorpe put_device(&stdev->dev); 1705080b47deSLogan Gunthorpe } 1706080b47deSLogan Gunthorpe 1707b13313a0SLogan Gunthorpe #define SWITCHTEC_PCI_DEVICE(device_id, gen) \ 1708080b47deSLogan Gunthorpe { \ 1709cfdfc14eSDoug Meyer .vendor = PCI_VENDOR_ID_MICROSEMI, \ 1710080b47deSLogan Gunthorpe .device = device_id, \ 1711080b47deSLogan Gunthorpe .subvendor = PCI_ANY_ID, \ 1712080b47deSLogan Gunthorpe .subdevice = PCI_ANY_ID, \ 1713cfdfc14eSDoug Meyer .class = (PCI_CLASS_MEMORY_OTHER << 8), \ 1714080b47deSLogan Gunthorpe .class_mask = 0xFFFFFFFF, \ 1715b13313a0SLogan Gunthorpe .driver_data = gen, \ 1716080b47deSLogan Gunthorpe }, \ 1717080b47deSLogan Gunthorpe { \ 1718cfdfc14eSDoug Meyer .vendor = PCI_VENDOR_ID_MICROSEMI, \ 1719080b47deSLogan Gunthorpe .device = device_id, \ 1720080b47deSLogan Gunthorpe .subvendor = PCI_ANY_ID, \ 1721080b47deSLogan Gunthorpe .subdevice = PCI_ANY_ID, \ 1722cfdfc14eSDoug Meyer .class = (PCI_CLASS_BRIDGE_OTHER << 8), \ 1723080b47deSLogan Gunthorpe .class_mask = 0xFFFFFFFF, \ 1724b13313a0SLogan Gunthorpe .driver_data = gen, \ 1725080b47deSLogan Gunthorpe } 1726080b47deSLogan Gunthorpe 1727080b47deSLogan Gunthorpe static const struct pci_device_id switchtec_pci_tbl[] = { 1728b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8531, SWITCHTEC_GEN3), //PFX 24xG3 1729b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8532, SWITCHTEC_GEN3), //PFX 32xG3 1730b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8533, SWITCHTEC_GEN3), //PFX 48xG3 1731b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8534, SWITCHTEC_GEN3), //PFX 64xG3 1732b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8535, SWITCHTEC_GEN3), //PFX 80xG3 1733b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8536, SWITCHTEC_GEN3), //PFX 96xG3 1734b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8541, SWITCHTEC_GEN3), //PSX 24xG3 1735b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8542, SWITCHTEC_GEN3), //PSX 32xG3 1736b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8543, SWITCHTEC_GEN3), //PSX 48xG3 1737b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8544, SWITCHTEC_GEN3), //PSX 64xG3 1738b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8545, SWITCHTEC_GEN3), //PSX 80xG3 1739b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8546, SWITCHTEC_GEN3), //PSX 96xG3 1740b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8551, SWITCHTEC_GEN3), //PAX 24XG3 1741b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8552, SWITCHTEC_GEN3), //PAX 32XG3 1742b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8553, SWITCHTEC_GEN3), //PAX 48XG3 1743b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8554, SWITCHTEC_GEN3), //PAX 64XG3 1744b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8555, SWITCHTEC_GEN3), //PAX 80XG3 1745b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8556, SWITCHTEC_GEN3), //PAX 96XG3 1746b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8561, SWITCHTEC_GEN3), //PFXL 24XG3 1747b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8562, SWITCHTEC_GEN3), //PFXL 32XG3 1748b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8563, SWITCHTEC_GEN3), //PFXL 48XG3 1749b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8564, SWITCHTEC_GEN3), //PFXL 64XG3 1750b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8565, SWITCHTEC_GEN3), //PFXL 80XG3 1751b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8566, SWITCHTEC_GEN3), //PFXL 96XG3 1752b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8571, SWITCHTEC_GEN3), //PFXI 24XG3 1753b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8572, SWITCHTEC_GEN3), //PFXI 32XG3 1754b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8573, SWITCHTEC_GEN3), //PFXI 48XG3 1755b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8574, SWITCHTEC_GEN3), //PFXI 64XG3 1756b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8575, SWITCHTEC_GEN3), //PFXI 80XG3 1757b13313a0SLogan Gunthorpe SWITCHTEC_PCI_DEVICE(0x8576, SWITCHTEC_GEN3), //PFXI 96XG3 17587a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4000, SWITCHTEC_GEN4), //PFX 100XG4 17597a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4084, SWITCHTEC_GEN4), //PFX 84XG4 17607a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4068, SWITCHTEC_GEN4), //PFX 68XG4 17617a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4052, SWITCHTEC_GEN4), //PFX 52XG4 17627a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4036, SWITCHTEC_GEN4), //PFX 36XG4 17637a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4028, SWITCHTEC_GEN4), //PFX 28XG4 17647a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4100, SWITCHTEC_GEN4), //PSX 100XG4 17657a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4184, SWITCHTEC_GEN4), //PSX 84XG4 17667a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4168, SWITCHTEC_GEN4), //PSX 68XG4 17677a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4152, SWITCHTEC_GEN4), //PSX 52XG4 17687a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4136, SWITCHTEC_GEN4), //PSX 36XG4 17697a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4128, SWITCHTEC_GEN4), //PSX 28XG4 17707a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4200, SWITCHTEC_GEN4), //PAX 100XG4 17717a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4284, SWITCHTEC_GEN4), //PAX 84XG4 17727a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4268, SWITCHTEC_GEN4), //PAX 68XG4 17737a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4252, SWITCHTEC_GEN4), //PAX 52XG4 17747a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4236, SWITCHTEC_GEN4), //PAX 36XG4 17757a30ebb9SKelvin Cao SWITCHTEC_PCI_DEVICE(0x4228, SWITCHTEC_GEN4), //PAX 28XG4 1776080b47deSLogan Gunthorpe {0} 1777080b47deSLogan Gunthorpe }; 1778080b47deSLogan Gunthorpe MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl); 1779080b47deSLogan Gunthorpe 1780080b47deSLogan Gunthorpe static struct pci_driver switchtec_pci_driver = { 1781080b47deSLogan Gunthorpe .name = KBUILD_MODNAME, 1782080b47deSLogan Gunthorpe .id_table = switchtec_pci_tbl, 1783080b47deSLogan Gunthorpe .probe = switchtec_pci_probe, 1784080b47deSLogan Gunthorpe .remove = switchtec_pci_remove, 1785080b47deSLogan Gunthorpe }; 1786080b47deSLogan Gunthorpe 1787080b47deSLogan Gunthorpe static int __init switchtec_init(void) 1788080b47deSLogan Gunthorpe { 1789080b47deSLogan Gunthorpe int rc; 1790080b47deSLogan Gunthorpe 1791080b47deSLogan Gunthorpe rc = alloc_chrdev_region(&switchtec_devt, 0, max_devices, 1792080b47deSLogan Gunthorpe "switchtec"); 1793080b47deSLogan Gunthorpe if (rc) 1794080b47deSLogan Gunthorpe return rc; 1795080b47deSLogan Gunthorpe 1796080b47deSLogan Gunthorpe switchtec_class = class_create(THIS_MODULE, "switchtec"); 1797080b47deSLogan Gunthorpe if (IS_ERR(switchtec_class)) { 1798080b47deSLogan Gunthorpe rc = PTR_ERR(switchtec_class); 1799080b47deSLogan Gunthorpe goto err_create_class; 1800080b47deSLogan Gunthorpe } 1801080b47deSLogan Gunthorpe 1802080b47deSLogan Gunthorpe rc = pci_register_driver(&switchtec_pci_driver); 1803080b47deSLogan Gunthorpe if (rc) 1804080b47deSLogan Gunthorpe goto err_pci_register; 1805080b47deSLogan Gunthorpe 1806080b47deSLogan Gunthorpe pr_info(KBUILD_MODNAME ": loaded.\n"); 1807080b47deSLogan Gunthorpe 1808080b47deSLogan Gunthorpe return 0; 1809080b47deSLogan Gunthorpe 1810080b47deSLogan Gunthorpe err_pci_register: 1811080b47deSLogan Gunthorpe class_destroy(switchtec_class); 1812080b47deSLogan Gunthorpe 1813080b47deSLogan Gunthorpe err_create_class: 1814080b47deSLogan Gunthorpe unregister_chrdev_region(switchtec_devt, max_devices); 1815080b47deSLogan Gunthorpe 1816080b47deSLogan Gunthorpe return rc; 1817080b47deSLogan Gunthorpe } 1818080b47deSLogan Gunthorpe module_init(switchtec_init); 1819080b47deSLogan Gunthorpe 1820080b47deSLogan Gunthorpe static void __exit switchtec_exit(void) 1821080b47deSLogan Gunthorpe { 1822080b47deSLogan Gunthorpe pci_unregister_driver(&switchtec_pci_driver); 1823080b47deSLogan Gunthorpe class_destroy(switchtec_class); 1824080b47deSLogan Gunthorpe unregister_chrdev_region(switchtec_devt, max_devices); 1825080b47deSLogan Gunthorpe ida_destroy(&switchtec_minor_ida); 1826080b47deSLogan Gunthorpe 1827080b47deSLogan Gunthorpe pr_info(KBUILD_MODNAME ": unloaded.\n"); 1828080b47deSLogan Gunthorpe } 1829080b47deSLogan Gunthorpe module_exit(switchtec_exit); 1830