1f204e0b8SIan Munsie /* 2f204e0b8SIan Munsie * Copyright 2014 IBM Corp. 3f204e0b8SIan Munsie * 4f204e0b8SIan Munsie * This program is free software; you can redistribute it and/or 5f204e0b8SIan Munsie * modify it under the terms of the GNU General Public License 6f204e0b8SIan Munsie * as published by the Free Software Foundation; either version 7f204e0b8SIan Munsie * 2 of the License, or (at your option) any later version. 8f204e0b8SIan Munsie */ 9f204e0b8SIan Munsie 10f204e0b8SIan Munsie #include <linux/kernel.h> 11f204e0b8SIan Munsie #include <linux/device.h> 12f204e0b8SIan Munsie #include <linux/sysfs.h> 13*b087e619SIan Munsie #include <linux/pci_regs.h> 14f204e0b8SIan Munsie 15f204e0b8SIan Munsie #include "cxl.h" 16f204e0b8SIan Munsie 17f204e0b8SIan Munsie #define to_afu_chardev_m(d) dev_get_drvdata(d) 18f204e0b8SIan Munsie 19f204e0b8SIan Munsie /********* Adapter attributes **********************************************/ 20f204e0b8SIan Munsie 21f204e0b8SIan Munsie static ssize_t caia_version_show(struct device *device, 22f204e0b8SIan Munsie struct device_attribute *attr, 23f204e0b8SIan Munsie char *buf) 24f204e0b8SIan Munsie { 25f204e0b8SIan Munsie struct cxl *adapter = to_cxl_adapter(device); 26f204e0b8SIan Munsie 27f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major, 28f204e0b8SIan Munsie adapter->caia_minor); 29f204e0b8SIan Munsie } 30f204e0b8SIan Munsie 31f204e0b8SIan Munsie static ssize_t psl_revision_show(struct device *device, 32f204e0b8SIan Munsie struct device_attribute *attr, 33f204e0b8SIan Munsie char *buf) 34f204e0b8SIan Munsie { 35f204e0b8SIan Munsie struct cxl *adapter = to_cxl_adapter(device); 36f204e0b8SIan Munsie 37f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev); 38f204e0b8SIan Munsie } 39f204e0b8SIan Munsie 40f204e0b8SIan Munsie static ssize_t base_image_show(struct device *device, 41f204e0b8SIan Munsie struct device_attribute *attr, 42f204e0b8SIan Munsie char *buf) 43f204e0b8SIan Munsie { 44f204e0b8SIan Munsie struct cxl *adapter = to_cxl_adapter(device); 45f204e0b8SIan Munsie 46f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image); 47f204e0b8SIan Munsie } 48f204e0b8SIan Munsie 49f204e0b8SIan Munsie static ssize_t image_loaded_show(struct device *device, 50f204e0b8SIan Munsie struct device_attribute *attr, 51f204e0b8SIan Munsie char *buf) 52f204e0b8SIan Munsie { 53f204e0b8SIan Munsie struct cxl *adapter = to_cxl_adapter(device); 54f204e0b8SIan Munsie 55f204e0b8SIan Munsie if (adapter->user_image_loaded) 56f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "user\n"); 57f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "factory\n"); 58f204e0b8SIan Munsie } 59f204e0b8SIan Munsie 6062fa19d4SRyan Grimm static ssize_t reset_adapter_store(struct device *device, 6162fa19d4SRyan Grimm struct device_attribute *attr, 6262fa19d4SRyan Grimm const char *buf, size_t count) 6362fa19d4SRyan Grimm { 6462fa19d4SRyan Grimm struct cxl *adapter = to_cxl_adapter(device); 6562fa19d4SRyan Grimm int rc; 6662fa19d4SRyan Grimm int val; 6762fa19d4SRyan Grimm 6862fa19d4SRyan Grimm rc = sscanf(buf, "%i", &val); 6962fa19d4SRyan Grimm if ((rc != 1) || (val != 1)) 7062fa19d4SRyan Grimm return -EINVAL; 7162fa19d4SRyan Grimm 7262fa19d4SRyan Grimm if ((rc = cxl_reset(adapter))) 7362fa19d4SRyan Grimm return rc; 7462fa19d4SRyan Grimm return count; 7562fa19d4SRyan Grimm } 7662fa19d4SRyan Grimm 7795bc11bcSRyan Grimm static ssize_t load_image_on_perst_show(struct device *device, 7895bc11bcSRyan Grimm struct device_attribute *attr, 7995bc11bcSRyan Grimm char *buf) 8095bc11bcSRyan Grimm { 8195bc11bcSRyan Grimm struct cxl *adapter = to_cxl_adapter(device); 8295bc11bcSRyan Grimm 8395bc11bcSRyan Grimm if (!adapter->perst_loads_image) 8495bc11bcSRyan Grimm return scnprintf(buf, PAGE_SIZE, "none\n"); 8595bc11bcSRyan Grimm 8695bc11bcSRyan Grimm if (adapter->perst_select_user) 8795bc11bcSRyan Grimm return scnprintf(buf, PAGE_SIZE, "user\n"); 8895bc11bcSRyan Grimm return scnprintf(buf, PAGE_SIZE, "factory\n"); 8995bc11bcSRyan Grimm } 9095bc11bcSRyan Grimm 9195bc11bcSRyan Grimm static ssize_t load_image_on_perst_store(struct device *device, 9295bc11bcSRyan Grimm struct device_attribute *attr, 9395bc11bcSRyan Grimm const char *buf, size_t count) 9495bc11bcSRyan Grimm { 9595bc11bcSRyan Grimm struct cxl *adapter = to_cxl_adapter(device); 9695bc11bcSRyan Grimm int rc; 9795bc11bcSRyan Grimm 9895bc11bcSRyan Grimm if (!strncmp(buf, "none", 4)) 9995bc11bcSRyan Grimm adapter->perst_loads_image = false; 10095bc11bcSRyan Grimm else if (!strncmp(buf, "user", 4)) { 10195bc11bcSRyan Grimm adapter->perst_select_user = true; 10295bc11bcSRyan Grimm adapter->perst_loads_image = true; 10395bc11bcSRyan Grimm } else if (!strncmp(buf, "factory", 7)) { 10495bc11bcSRyan Grimm adapter->perst_select_user = false; 10595bc11bcSRyan Grimm adapter->perst_loads_image = true; 10695bc11bcSRyan Grimm } else 10795bc11bcSRyan Grimm return -EINVAL; 10895bc11bcSRyan Grimm 10995bc11bcSRyan Grimm if ((rc = cxl_update_image_control(adapter))) 11095bc11bcSRyan Grimm return rc; 11195bc11bcSRyan Grimm 11295bc11bcSRyan Grimm return count; 11395bc11bcSRyan Grimm } 11495bc11bcSRyan Grimm 115f204e0b8SIan Munsie static struct device_attribute adapter_attrs[] = { 116f204e0b8SIan Munsie __ATTR_RO(caia_version), 117f204e0b8SIan Munsie __ATTR_RO(psl_revision), 118f204e0b8SIan Munsie __ATTR_RO(base_image), 119f204e0b8SIan Munsie __ATTR_RO(image_loaded), 12095bc11bcSRyan Grimm __ATTR_RW(load_image_on_perst), 12162fa19d4SRyan Grimm __ATTR(reset, S_IWUSR, NULL, reset_adapter_store), 122f204e0b8SIan Munsie }; 123f204e0b8SIan Munsie 124f204e0b8SIan Munsie 125f204e0b8SIan Munsie /********* AFU master specific attributes **********************************/ 126f204e0b8SIan Munsie 127f204e0b8SIan Munsie static ssize_t mmio_size_show_master(struct device *device, 128f204e0b8SIan Munsie struct device_attribute *attr, 129f204e0b8SIan Munsie char *buf) 130f204e0b8SIan Munsie { 131f204e0b8SIan Munsie struct cxl_afu *afu = to_afu_chardev_m(device); 132f204e0b8SIan Munsie 133f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); 134f204e0b8SIan Munsie } 135f204e0b8SIan Munsie 136f204e0b8SIan Munsie static ssize_t pp_mmio_off_show(struct device *device, 137f204e0b8SIan Munsie struct device_attribute *attr, 138f204e0b8SIan Munsie char *buf) 139f204e0b8SIan Munsie { 140f204e0b8SIan Munsie struct cxl_afu *afu = to_afu_chardev_m(device); 141f204e0b8SIan Munsie 142f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_offset); 143f204e0b8SIan Munsie } 144f204e0b8SIan Munsie 145f204e0b8SIan Munsie static ssize_t pp_mmio_len_show(struct device *device, 146f204e0b8SIan Munsie struct device_attribute *attr, 147f204e0b8SIan Munsie char *buf) 148f204e0b8SIan Munsie { 149f204e0b8SIan Munsie struct cxl_afu *afu = to_afu_chardev_m(device); 150f204e0b8SIan Munsie 151f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); 152f204e0b8SIan Munsie } 153f204e0b8SIan Munsie 154f204e0b8SIan Munsie static struct device_attribute afu_master_attrs[] = { 155f204e0b8SIan Munsie __ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL), 156f204e0b8SIan Munsie __ATTR_RO(pp_mmio_off), 157f204e0b8SIan Munsie __ATTR_RO(pp_mmio_len), 158f204e0b8SIan Munsie }; 159f204e0b8SIan Munsie 160f204e0b8SIan Munsie 161f204e0b8SIan Munsie /********* AFU attributes **************************************************/ 162f204e0b8SIan Munsie 163f204e0b8SIan Munsie static ssize_t mmio_size_show(struct device *device, 164f204e0b8SIan Munsie struct device_attribute *attr, 165f204e0b8SIan Munsie char *buf) 166f204e0b8SIan Munsie { 167f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 168f204e0b8SIan Munsie 169f204e0b8SIan Munsie if (afu->pp_size) 170f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); 171f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); 172f204e0b8SIan Munsie } 173f204e0b8SIan Munsie 174f204e0b8SIan Munsie static ssize_t reset_store_afu(struct device *device, 175f204e0b8SIan Munsie struct device_attribute *attr, 176f204e0b8SIan Munsie const char *buf, size_t count) 177f204e0b8SIan Munsie { 178f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 179f204e0b8SIan Munsie int rc; 180f204e0b8SIan Munsie 181f204e0b8SIan Munsie /* Not safe to reset if it is currently in use */ 182ee41d11dSIan Munsie mutex_lock(&afu->contexts_lock); 183f204e0b8SIan Munsie if (!idr_is_empty(&afu->contexts_idr)) { 184f204e0b8SIan Munsie rc = -EBUSY; 185f204e0b8SIan Munsie goto err; 186f204e0b8SIan Munsie } 187f204e0b8SIan Munsie 188f204e0b8SIan Munsie if ((rc = cxl_afu_reset(afu))) 189f204e0b8SIan Munsie goto err; 190f204e0b8SIan Munsie 191f204e0b8SIan Munsie rc = count; 192f204e0b8SIan Munsie err: 193ee41d11dSIan Munsie mutex_unlock(&afu->contexts_lock); 194f204e0b8SIan Munsie return rc; 195f204e0b8SIan Munsie } 196f204e0b8SIan Munsie 197f204e0b8SIan Munsie static ssize_t irqs_min_show(struct device *device, 198f204e0b8SIan Munsie struct device_attribute *attr, 199f204e0b8SIan Munsie char *buf) 200f204e0b8SIan Munsie { 201f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 202f204e0b8SIan Munsie 203f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs); 204f204e0b8SIan Munsie } 205f204e0b8SIan Munsie 206f204e0b8SIan Munsie static ssize_t irqs_max_show(struct device *device, 207f204e0b8SIan Munsie struct device_attribute *attr, 208f204e0b8SIan Munsie char *buf) 209f204e0b8SIan Munsie { 210f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 211f204e0b8SIan Munsie 212f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max); 213f204e0b8SIan Munsie } 214f204e0b8SIan Munsie 215f204e0b8SIan Munsie static ssize_t irqs_max_store(struct device *device, 216f204e0b8SIan Munsie struct device_attribute *attr, 217f204e0b8SIan Munsie const char *buf, size_t count) 218f204e0b8SIan Munsie { 219f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 220f204e0b8SIan Munsie ssize_t ret; 221f204e0b8SIan Munsie int irqs_max; 222f204e0b8SIan Munsie 223f204e0b8SIan Munsie ret = sscanf(buf, "%i", &irqs_max); 224f204e0b8SIan Munsie if (ret != 1) 225f204e0b8SIan Munsie return -EINVAL; 226f204e0b8SIan Munsie 227f204e0b8SIan Munsie if (irqs_max < afu->pp_irqs) 228f204e0b8SIan Munsie return -EINVAL; 229f204e0b8SIan Munsie 230f204e0b8SIan Munsie if (irqs_max > afu->adapter->user_irqs) 231f204e0b8SIan Munsie return -EINVAL; 232f204e0b8SIan Munsie 233f204e0b8SIan Munsie afu->irqs_max = irqs_max; 234f204e0b8SIan Munsie return count; 235f204e0b8SIan Munsie } 236f204e0b8SIan Munsie 237f204e0b8SIan Munsie static ssize_t modes_supported_show(struct device *device, 238f204e0b8SIan Munsie struct device_attribute *attr, char *buf) 239f204e0b8SIan Munsie { 240f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 241f204e0b8SIan Munsie char *p = buf, *end = buf + PAGE_SIZE; 242f204e0b8SIan Munsie 243f204e0b8SIan Munsie if (afu->modes_supported & CXL_MODE_DEDICATED) 244f204e0b8SIan Munsie p += scnprintf(p, end - p, "dedicated_process\n"); 245f204e0b8SIan Munsie if (afu->modes_supported & CXL_MODE_DIRECTED) 246f204e0b8SIan Munsie p += scnprintf(p, end - p, "afu_directed\n"); 247f204e0b8SIan Munsie return (p - buf); 248f204e0b8SIan Munsie } 249f204e0b8SIan Munsie 250f204e0b8SIan Munsie static ssize_t prefault_mode_show(struct device *device, 251f204e0b8SIan Munsie struct device_attribute *attr, 252f204e0b8SIan Munsie char *buf) 253f204e0b8SIan Munsie { 254f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 255f204e0b8SIan Munsie 256f204e0b8SIan Munsie switch (afu->prefault_mode) { 257f204e0b8SIan Munsie case CXL_PREFAULT_WED: 258f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n"); 259f204e0b8SIan Munsie case CXL_PREFAULT_ALL: 260f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "all\n"); 261f204e0b8SIan Munsie default: 262f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "none\n"); 263f204e0b8SIan Munsie } 264f204e0b8SIan Munsie } 265f204e0b8SIan Munsie 266f204e0b8SIan Munsie static ssize_t prefault_mode_store(struct device *device, 267f204e0b8SIan Munsie struct device_attribute *attr, 268f204e0b8SIan Munsie const char *buf, size_t count) 269f204e0b8SIan Munsie { 270f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 271f204e0b8SIan Munsie enum prefault_modes mode = -1; 272f204e0b8SIan Munsie 273f204e0b8SIan Munsie if (!strncmp(buf, "work_element_descriptor", 23)) 274f204e0b8SIan Munsie mode = CXL_PREFAULT_WED; 275f204e0b8SIan Munsie if (!strncmp(buf, "all", 3)) 276f204e0b8SIan Munsie mode = CXL_PREFAULT_ALL; 277f204e0b8SIan Munsie if (!strncmp(buf, "none", 4)) 278f204e0b8SIan Munsie mode = CXL_PREFAULT_NONE; 279f204e0b8SIan Munsie 280f204e0b8SIan Munsie if (mode == -1) 281f204e0b8SIan Munsie return -EINVAL; 282f204e0b8SIan Munsie 283f204e0b8SIan Munsie afu->prefault_mode = mode; 284f204e0b8SIan Munsie return count; 285f204e0b8SIan Munsie } 286f204e0b8SIan Munsie 287f204e0b8SIan Munsie static ssize_t mode_show(struct device *device, 288f204e0b8SIan Munsie struct device_attribute *attr, 289f204e0b8SIan Munsie char *buf) 290f204e0b8SIan Munsie { 291f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 292f204e0b8SIan Munsie 293f204e0b8SIan Munsie if (afu->current_mode == CXL_MODE_DEDICATED) 294f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "dedicated_process\n"); 295f204e0b8SIan Munsie if (afu->current_mode == CXL_MODE_DIRECTED) 296f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "afu_directed\n"); 297f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "none\n"); 298f204e0b8SIan Munsie } 299f204e0b8SIan Munsie 300f204e0b8SIan Munsie static ssize_t mode_store(struct device *device, struct device_attribute *attr, 301f204e0b8SIan Munsie const char *buf, size_t count) 302f204e0b8SIan Munsie { 303f204e0b8SIan Munsie struct cxl_afu *afu = to_cxl_afu(device); 304f204e0b8SIan Munsie int old_mode, mode = -1; 305f204e0b8SIan Munsie int rc = -EBUSY; 306f204e0b8SIan Munsie 307f204e0b8SIan Munsie /* can't change this if we have a user */ 308ee41d11dSIan Munsie mutex_lock(&afu->contexts_lock); 309f204e0b8SIan Munsie if (!idr_is_empty(&afu->contexts_idr)) 310f204e0b8SIan Munsie goto err; 311f204e0b8SIan Munsie 312f204e0b8SIan Munsie if (!strncmp(buf, "dedicated_process", 17)) 313f204e0b8SIan Munsie mode = CXL_MODE_DEDICATED; 314f204e0b8SIan Munsie if (!strncmp(buf, "afu_directed", 12)) 315f204e0b8SIan Munsie mode = CXL_MODE_DIRECTED; 316f204e0b8SIan Munsie if (!strncmp(buf, "none", 4)) 317f204e0b8SIan Munsie mode = 0; 318f204e0b8SIan Munsie 319f204e0b8SIan Munsie if (mode == -1) { 320f204e0b8SIan Munsie rc = -EINVAL; 321f204e0b8SIan Munsie goto err; 322f204e0b8SIan Munsie } 323f204e0b8SIan Munsie 324f204e0b8SIan Munsie /* 325f204e0b8SIan Munsie * cxl_afu_deactivate_mode needs to be done outside the lock, prevent 326f204e0b8SIan Munsie * other contexts coming in before we are ready: 327f204e0b8SIan Munsie */ 328f204e0b8SIan Munsie old_mode = afu->current_mode; 329f204e0b8SIan Munsie afu->current_mode = 0; 330f204e0b8SIan Munsie afu->num_procs = 0; 331f204e0b8SIan Munsie 332ee41d11dSIan Munsie mutex_unlock(&afu->contexts_lock); 333f204e0b8SIan Munsie 334f204e0b8SIan Munsie if ((rc = _cxl_afu_deactivate_mode(afu, old_mode))) 335f204e0b8SIan Munsie return rc; 336f204e0b8SIan Munsie if ((rc = cxl_afu_activate_mode(afu, mode))) 337f204e0b8SIan Munsie return rc; 338f204e0b8SIan Munsie 339f204e0b8SIan Munsie return count; 340f204e0b8SIan Munsie err: 341ee41d11dSIan Munsie mutex_unlock(&afu->contexts_lock); 342f204e0b8SIan Munsie return rc; 343f204e0b8SIan Munsie } 344f204e0b8SIan Munsie 345f204e0b8SIan Munsie static ssize_t api_version_show(struct device *device, 346f204e0b8SIan Munsie struct device_attribute *attr, 347f204e0b8SIan Munsie char *buf) 348f204e0b8SIan Munsie { 349f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION); 350f204e0b8SIan Munsie } 351f204e0b8SIan Munsie 352f204e0b8SIan Munsie static ssize_t api_version_compatible_show(struct device *device, 353f204e0b8SIan Munsie struct device_attribute *attr, 354f204e0b8SIan Munsie char *buf) 355f204e0b8SIan Munsie { 356f204e0b8SIan Munsie return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE); 357f204e0b8SIan Munsie } 358f204e0b8SIan Munsie 359f204e0b8SIan Munsie static struct device_attribute afu_attrs[] = { 360f204e0b8SIan Munsie __ATTR_RO(mmio_size), 361f204e0b8SIan Munsie __ATTR_RO(irqs_min), 362f204e0b8SIan Munsie __ATTR_RW(irqs_max), 363f204e0b8SIan Munsie __ATTR_RO(modes_supported), 364f204e0b8SIan Munsie __ATTR_RW(mode), 365f204e0b8SIan Munsie __ATTR_RW(prefault_mode), 366f204e0b8SIan Munsie __ATTR_RO(api_version), 367f204e0b8SIan Munsie __ATTR_RO(api_version_compatible), 368f204e0b8SIan Munsie __ATTR(reset, S_IWUSR, NULL, reset_store_afu), 369f204e0b8SIan Munsie }; 370f204e0b8SIan Munsie 371f204e0b8SIan Munsie int cxl_sysfs_adapter_add(struct cxl *adapter) 372f204e0b8SIan Munsie { 373f204e0b8SIan Munsie int i, rc; 374f204e0b8SIan Munsie 375f204e0b8SIan Munsie for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) { 376f204e0b8SIan Munsie if ((rc = device_create_file(&adapter->dev, &adapter_attrs[i]))) 377f204e0b8SIan Munsie goto err; 378f204e0b8SIan Munsie } 379f204e0b8SIan Munsie return 0; 380f204e0b8SIan Munsie err: 381f204e0b8SIan Munsie for (i--; i >= 0; i--) 382f204e0b8SIan Munsie device_remove_file(&adapter->dev, &adapter_attrs[i]); 383f204e0b8SIan Munsie return rc; 384f204e0b8SIan Munsie } 385f204e0b8SIan Munsie void cxl_sysfs_adapter_remove(struct cxl *adapter) 386f204e0b8SIan Munsie { 387f204e0b8SIan Munsie int i; 388f204e0b8SIan Munsie 389f204e0b8SIan Munsie for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) 390f204e0b8SIan Munsie device_remove_file(&adapter->dev, &adapter_attrs[i]); 391f204e0b8SIan Munsie } 392f204e0b8SIan Munsie 393*b087e619SIan Munsie struct afu_config_record { 394*b087e619SIan Munsie struct kobject kobj; 395*b087e619SIan Munsie struct bin_attribute config_attr; 396*b087e619SIan Munsie struct list_head list; 397*b087e619SIan Munsie int cr; 398*b087e619SIan Munsie u16 device; 399*b087e619SIan Munsie u16 vendor; 400*b087e619SIan Munsie u32 class; 401*b087e619SIan Munsie }; 402*b087e619SIan Munsie 403*b087e619SIan Munsie #define to_cr(obj) container_of(obj, struct afu_config_record, kobj) 404*b087e619SIan Munsie 405*b087e619SIan Munsie static ssize_t vendor_show(struct kobject *kobj, 406*b087e619SIan Munsie struct kobj_attribute *attr, char *buf) 407*b087e619SIan Munsie { 408*b087e619SIan Munsie struct afu_config_record *cr = to_cr(kobj); 409*b087e619SIan Munsie 410*b087e619SIan Munsie return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->vendor); 411*b087e619SIan Munsie } 412*b087e619SIan Munsie 413*b087e619SIan Munsie static ssize_t device_show(struct kobject *kobj, 414*b087e619SIan Munsie struct kobj_attribute *attr, char *buf) 415*b087e619SIan Munsie { 416*b087e619SIan Munsie struct afu_config_record *cr = to_cr(kobj); 417*b087e619SIan Munsie 418*b087e619SIan Munsie return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->device); 419*b087e619SIan Munsie } 420*b087e619SIan Munsie 421*b087e619SIan Munsie static ssize_t class_show(struct kobject *kobj, 422*b087e619SIan Munsie struct kobj_attribute *attr, char *buf) 423*b087e619SIan Munsie { 424*b087e619SIan Munsie struct afu_config_record *cr = to_cr(kobj); 425*b087e619SIan Munsie 426*b087e619SIan Munsie return scnprintf(buf, PAGE_SIZE, "0x%.6x\n", cr->class); 427*b087e619SIan Munsie } 428*b087e619SIan Munsie 429*b087e619SIan Munsie static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, 430*b087e619SIan Munsie struct bin_attribute *bin_attr, char *buf, 431*b087e619SIan Munsie loff_t off, size_t count) 432*b087e619SIan Munsie { 433*b087e619SIan Munsie struct afu_config_record *cr = to_cr(kobj); 434*b087e619SIan Munsie struct cxl_afu *afu = to_cxl_afu(container_of(kobj->parent, struct device, kobj)); 435*b087e619SIan Munsie 436*b087e619SIan Munsie u64 i, j, val, size = afu->crs_len; 437*b087e619SIan Munsie 438*b087e619SIan Munsie if (off > size) 439*b087e619SIan Munsie return 0; 440*b087e619SIan Munsie if (off + count > size) 441*b087e619SIan Munsie count = size - off; 442*b087e619SIan Munsie 443*b087e619SIan Munsie for (i = 0; i < count;) { 444*b087e619SIan Munsie val = cxl_afu_cr_read64(afu, cr->cr, off & ~0x7); 445*b087e619SIan Munsie for (j = off & 0x7; j < 8 && i < count; i++, j++, off++) 446*b087e619SIan Munsie buf[i] = (val >> (j * 8)) & 0xff; 447*b087e619SIan Munsie } 448*b087e619SIan Munsie 449*b087e619SIan Munsie return count; 450*b087e619SIan Munsie } 451*b087e619SIan Munsie 452*b087e619SIan Munsie static struct kobj_attribute vendor_attribute = 453*b087e619SIan Munsie __ATTR_RO(vendor); 454*b087e619SIan Munsie static struct kobj_attribute device_attribute = 455*b087e619SIan Munsie __ATTR_RO(device); 456*b087e619SIan Munsie static struct kobj_attribute class_attribute = 457*b087e619SIan Munsie __ATTR_RO(class); 458*b087e619SIan Munsie 459*b087e619SIan Munsie static struct attribute *afu_cr_attrs[] = { 460*b087e619SIan Munsie &vendor_attribute.attr, 461*b087e619SIan Munsie &device_attribute.attr, 462*b087e619SIan Munsie &class_attribute.attr, 463*b087e619SIan Munsie NULL, 464*b087e619SIan Munsie }; 465*b087e619SIan Munsie 466*b087e619SIan Munsie static void release_afu_config_record(struct kobject *kobj) 467*b087e619SIan Munsie { 468*b087e619SIan Munsie struct afu_config_record *cr = to_cr(kobj); 469*b087e619SIan Munsie 470*b087e619SIan Munsie kfree(cr); 471*b087e619SIan Munsie } 472*b087e619SIan Munsie 473*b087e619SIan Munsie static struct kobj_type afu_config_record_type = { 474*b087e619SIan Munsie .sysfs_ops = &kobj_sysfs_ops, 475*b087e619SIan Munsie .release = release_afu_config_record, 476*b087e619SIan Munsie .default_attrs = afu_cr_attrs, 477*b087e619SIan Munsie }; 478*b087e619SIan Munsie 479*b087e619SIan Munsie static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx) 480*b087e619SIan Munsie { 481*b087e619SIan Munsie struct afu_config_record *cr; 482*b087e619SIan Munsie int rc; 483*b087e619SIan Munsie 484*b087e619SIan Munsie cr = kzalloc(sizeof(struct afu_config_record), GFP_KERNEL); 485*b087e619SIan Munsie if (!cr) 486*b087e619SIan Munsie return ERR_PTR(-ENOMEM); 487*b087e619SIan Munsie 488*b087e619SIan Munsie cr->cr = cr_idx; 489*b087e619SIan Munsie cr->device = cxl_afu_cr_read16(afu, cr_idx, PCI_DEVICE_ID); 490*b087e619SIan Munsie cr->vendor = cxl_afu_cr_read16(afu, cr_idx, PCI_VENDOR_ID); 491*b087e619SIan Munsie cr->class = cxl_afu_cr_read32(afu, cr_idx, PCI_CLASS_REVISION) >> 8; 492*b087e619SIan Munsie 493*b087e619SIan Munsie /* 494*b087e619SIan Munsie * Export raw AFU PCIe like config record. For now this is read only by 495*b087e619SIan Munsie * root - we can expand that later to be readable by non-root and maybe 496*b087e619SIan Munsie * even writable provided we have a good use-case. Once we suport 497*b087e619SIan Munsie * exposing AFUs through a virtual PHB they will get that for free from 498*b087e619SIan Munsie * Linux' PCI infrastructure, but until then it's not clear that we 499*b087e619SIan Munsie * need it for anything since the main use case is just identifying 500*b087e619SIan Munsie * AFUs, which can be done via the vendor, device and class attributes. 501*b087e619SIan Munsie */ 502*b087e619SIan Munsie sysfs_bin_attr_init(&cr->config_attr); 503*b087e619SIan Munsie cr->config_attr.attr.name = "config"; 504*b087e619SIan Munsie cr->config_attr.attr.mode = S_IRUSR; 505*b087e619SIan Munsie cr->config_attr.size = afu->crs_len; 506*b087e619SIan Munsie cr->config_attr.read = afu_read_config; 507*b087e619SIan Munsie 508*b087e619SIan Munsie rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type, 509*b087e619SIan Munsie &afu->dev.kobj, "cr%i", cr->cr); 510*b087e619SIan Munsie if (rc) 511*b087e619SIan Munsie goto err; 512*b087e619SIan Munsie 513*b087e619SIan Munsie rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr); 514*b087e619SIan Munsie if (rc) 515*b087e619SIan Munsie goto err1; 516*b087e619SIan Munsie 517*b087e619SIan Munsie rc = kobject_uevent(&cr->kobj, KOBJ_ADD); 518*b087e619SIan Munsie if (rc) 519*b087e619SIan Munsie goto err2; 520*b087e619SIan Munsie 521*b087e619SIan Munsie return cr; 522*b087e619SIan Munsie err2: 523*b087e619SIan Munsie sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); 524*b087e619SIan Munsie err1: 525*b087e619SIan Munsie kobject_put(&cr->kobj); 526*b087e619SIan Munsie return ERR_PTR(rc); 527*b087e619SIan Munsie err: 528*b087e619SIan Munsie kfree(cr); 529*b087e619SIan Munsie return ERR_PTR(rc); 530*b087e619SIan Munsie } 531*b087e619SIan Munsie 532*b087e619SIan Munsie void cxl_sysfs_afu_remove(struct cxl_afu *afu) 533*b087e619SIan Munsie { 534*b087e619SIan Munsie struct afu_config_record *cr, *tmp; 535*b087e619SIan Munsie int i; 536*b087e619SIan Munsie 537*b087e619SIan Munsie for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) 538*b087e619SIan Munsie device_remove_file(&afu->dev, &afu_attrs[i]); 539*b087e619SIan Munsie 540*b087e619SIan Munsie list_for_each_entry_safe(cr, tmp, &afu->crs, list) { 541*b087e619SIan Munsie sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); 542*b087e619SIan Munsie kobject_put(&cr->kobj); 543*b087e619SIan Munsie } 544*b087e619SIan Munsie } 545*b087e619SIan Munsie 546f204e0b8SIan Munsie int cxl_sysfs_afu_add(struct cxl_afu *afu) 547f204e0b8SIan Munsie { 548*b087e619SIan Munsie struct afu_config_record *cr; 549f204e0b8SIan Munsie int i, rc; 550f204e0b8SIan Munsie 551*b087e619SIan Munsie INIT_LIST_HEAD(&afu->crs); 552*b087e619SIan Munsie 553f204e0b8SIan Munsie for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { 554f204e0b8SIan Munsie if ((rc = device_create_file(&afu->dev, &afu_attrs[i]))) 555f204e0b8SIan Munsie goto err; 556f204e0b8SIan Munsie } 557f204e0b8SIan Munsie 558*b087e619SIan Munsie for (i = 0; i < afu->crs_num; i++) { 559*b087e619SIan Munsie cr = cxl_sysfs_afu_new_cr(afu, i); 560*b087e619SIan Munsie if (IS_ERR(cr)) { 561*b087e619SIan Munsie rc = PTR_ERR(cr); 562*b087e619SIan Munsie goto err1; 563*b087e619SIan Munsie } 564*b087e619SIan Munsie list_add(&cr->list, &afu->crs); 565*b087e619SIan Munsie } 566*b087e619SIan Munsie 567f204e0b8SIan Munsie return 0; 568f204e0b8SIan Munsie 569*b087e619SIan Munsie err1: 570*b087e619SIan Munsie cxl_sysfs_afu_remove(afu); 571*b087e619SIan Munsie return rc; 572f204e0b8SIan Munsie err: 573f204e0b8SIan Munsie for (i--; i >= 0; i--) 574f204e0b8SIan Munsie device_remove_file(&afu->dev, &afu_attrs[i]); 575f204e0b8SIan Munsie return rc; 576f204e0b8SIan Munsie } 577f204e0b8SIan Munsie 578f204e0b8SIan Munsie int cxl_sysfs_afu_m_add(struct cxl_afu *afu) 579f204e0b8SIan Munsie { 580f204e0b8SIan Munsie int i, rc; 581f204e0b8SIan Munsie 582f204e0b8SIan Munsie for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) { 583f204e0b8SIan Munsie if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i]))) 584f204e0b8SIan Munsie goto err; 585f204e0b8SIan Munsie } 586f204e0b8SIan Munsie 587f204e0b8SIan Munsie return 0; 588f204e0b8SIan Munsie 589f204e0b8SIan Munsie err: 590f204e0b8SIan Munsie for (i--; i >= 0; i--) 591f204e0b8SIan Munsie device_remove_file(afu->chardev_m, &afu_master_attrs[i]); 592f204e0b8SIan Munsie return rc; 593f204e0b8SIan Munsie } 594f204e0b8SIan Munsie 595f204e0b8SIan Munsie void cxl_sysfs_afu_m_remove(struct cxl_afu *afu) 596f204e0b8SIan Munsie { 597f204e0b8SIan Munsie int i; 598f204e0b8SIan Munsie 599f204e0b8SIan Munsie for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) 600f204e0b8SIan Munsie device_remove_file(afu->chardev_m, &afu_master_attrs[i]); 601f204e0b8SIan Munsie } 602