janz-cmodio.c (7185B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Janz CMOD-IO MODULbus Carrier Board PCI Driver 4 * 5 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> 6 * 7 * Lots of inspiration and code was copied from drivers/mfd/sm501.c 8 */ 9 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/pci.h> 13#include <linux/interrupt.h> 14#include <linux/delay.h> 15#include <linux/platform_device.h> 16#include <linux/slab.h> 17#include <linux/mfd/core.h> 18 19#include <linux/mfd/janz.h> 20 21#define DRV_NAME "janz-cmodio" 22 23/* Size of each MODULbus module in PCI BAR4 */ 24#define CMODIO_MODULBUS_SIZE 0x200 25 26/* Maximum number of MODULbus modules on a CMOD-IO carrier board */ 27#define CMODIO_MAX_MODULES 4 28 29/* Module Parameters */ 30static unsigned int num_modules = CMODIO_MAX_MODULES; 31static char *modules[CMODIO_MAX_MODULES] = { 32 "empty", "empty", "empty", "empty", 33}; 34 35module_param_array(modules, charp, &num_modules, S_IRUGO); 36MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board"); 37 38/* Unique Device Id */ 39static unsigned int cmodio_id; 40 41struct cmodio_device { 42 /* Parent PCI device */ 43 struct pci_dev *pdev; 44 45 /* PLX control registers */ 46 struct janz_cmodio_onboard_regs __iomem *ctrl; 47 48 /* hex switch position */ 49 u8 hex; 50 51 /* mfd-core API */ 52 struct mfd_cell cells[CMODIO_MAX_MODULES]; 53 struct resource resources[3 * CMODIO_MAX_MODULES]; 54 struct janz_platform_data pdata[CMODIO_MAX_MODULES]; 55}; 56 57/* 58 * Subdevices using the mfd-core API 59 */ 60 61static int cmodio_setup_subdevice(struct cmodio_device *priv, 62 char *name, unsigned int devno, 63 unsigned int modno) 64{ 65 struct janz_platform_data *pdata; 66 struct mfd_cell *cell; 67 struct resource *res; 68 struct pci_dev *pci; 69 70 pci = priv->pdev; 71 cell = &priv->cells[devno]; 72 res = &priv->resources[devno * 3]; 73 pdata = &priv->pdata[devno]; 74 75 cell->name = name; 76 cell->resources = res; 77 cell->num_resources = 3; 78 79 /* Setup the subdevice ID -- must be unique */ 80 cell->id = cmodio_id++; 81 82 /* Add platform data */ 83 pdata->modno = modno; 84 cell->platform_data = pdata; 85 cell->pdata_size = sizeof(*pdata); 86 87 /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ 88 res->flags = IORESOURCE_MEM; 89 res->parent = &pci->resource[3]; 90 res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno); 91 res->end = res->start + CMODIO_MODULBUS_SIZE - 1; 92 res++; 93 94 /* PLX Control Registers -- PCI BAR4 is interrupt and other registers */ 95 res->flags = IORESOURCE_MEM; 96 res->parent = &pci->resource[4]; 97 res->start = pci->resource[4].start; 98 res->end = pci->resource[4].end; 99 res++; 100 101 /* 102 * IRQ 103 * 104 * The start and end fields are used as an offset to the irq_base 105 * parameter passed into the mfd_add_devices() function call. All 106 * devices share the same IRQ. 107 */ 108 res->flags = IORESOURCE_IRQ; 109 res->parent = NULL; 110 res->start = 0; 111 res->end = 0; 112 res++; 113 114 return 0; 115} 116 117/* Probe each submodule using kernel parameters */ 118static int cmodio_probe_submodules(struct cmodio_device *priv) 119{ 120 struct pci_dev *pdev = priv->pdev; 121 unsigned int num_probed = 0; 122 char *name; 123 int i; 124 125 for (i = 0; i < num_modules; i++) { 126 name = modules[i]; 127 if (!strcmp(name, "") || !strcmp(name, "empty")) 128 continue; 129 130 dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name); 131 cmodio_setup_subdevice(priv, name, num_probed, i); 132 num_probed++; 133 } 134 135 /* print an error message if no modules were probed */ 136 if (num_probed == 0) { 137 dev_err(&priv->pdev->dev, "no MODULbus modules specified, " 138 "please set the ``modules'' kernel " 139 "parameter according to your " 140 "hardware configuration\n"); 141 return -ENODEV; 142 } 143 144 return mfd_add_devices(&pdev->dev, 0, priv->cells, 145 num_probed, NULL, pdev->irq, NULL); 146} 147 148/* 149 * SYSFS Attributes 150 */ 151 152static ssize_t modulbus_number_show(struct device *dev, 153 struct device_attribute *attr, char *buf) 154{ 155 struct cmodio_device *priv = dev_get_drvdata(dev); 156 157 return sysfs_emit(buf, "%x\n", priv->hex); 158} 159 160static DEVICE_ATTR_RO(modulbus_number); 161 162static struct attribute *cmodio_sysfs_attrs[] = { 163 &dev_attr_modulbus_number.attr, 164 NULL, 165}; 166 167static const struct attribute_group cmodio_sysfs_attr_group = { 168 .attrs = cmodio_sysfs_attrs, 169}; 170 171/* 172 * PCI Driver 173 */ 174 175static int cmodio_pci_probe(struct pci_dev *dev, 176 const struct pci_device_id *id) 177{ 178 struct cmodio_device *priv; 179 int ret; 180 181 priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); 182 if (!priv) 183 return -ENOMEM; 184 185 pci_set_drvdata(dev, priv); 186 priv->pdev = dev; 187 188 /* Hardware Initialization */ 189 ret = pci_enable_device(dev); 190 if (ret) { 191 dev_err(&dev->dev, "unable to enable device\n"); 192 return ret; 193 } 194 195 pci_set_master(dev); 196 ret = pci_request_regions(dev, DRV_NAME); 197 if (ret) { 198 dev_err(&dev->dev, "unable to request regions\n"); 199 goto out_pci_disable_device; 200 } 201 202 /* Onboard configuration registers */ 203 priv->ctrl = pci_ioremap_bar(dev, 4); 204 if (!priv->ctrl) { 205 dev_err(&dev->dev, "unable to remap onboard regs\n"); 206 ret = -ENOMEM; 207 goto out_pci_release_regions; 208 } 209 210 /* Read the hex switch on the carrier board */ 211 priv->hex = ioread8(&priv->ctrl->int_enable); 212 213 /* Add the MODULbus number (hex switch value) to the device's sysfs */ 214 ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); 215 if (ret) { 216 dev_err(&dev->dev, "unable to create sysfs attributes\n"); 217 goto out_unmap_ctrl; 218 } 219 220 /* 221 * Disable all interrupt lines, each submodule will enable its 222 * own interrupt line if needed 223 */ 224 iowrite8(0xf, &priv->ctrl->int_disable); 225 226 /* Register drivers for all submodules */ 227 ret = cmodio_probe_submodules(priv); 228 if (ret) { 229 dev_err(&dev->dev, "unable to probe submodules\n"); 230 goto out_sysfs_remove_group; 231 } 232 233 return 0; 234 235out_sysfs_remove_group: 236 sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); 237out_unmap_ctrl: 238 iounmap(priv->ctrl); 239out_pci_release_regions: 240 pci_release_regions(dev); 241out_pci_disable_device: 242 pci_disable_device(dev); 243 244 return ret; 245} 246 247static void cmodio_pci_remove(struct pci_dev *dev) 248{ 249 struct cmodio_device *priv = pci_get_drvdata(dev); 250 251 mfd_remove_devices(&dev->dev); 252 sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); 253 iounmap(priv->ctrl); 254 pci_release_regions(dev); 255 pci_disable_device(dev); 256} 257 258#define PCI_VENDOR_ID_JANZ 0x13c3 259 260/* The list of devices that this module will support */ 261static const struct pci_device_id cmodio_pci_ids[] = { 262 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 }, 263 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 }, 264 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0201 }, 265 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0202 }, 266 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0201 }, 267 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0202 }, 268 { 0, } 269}; 270MODULE_DEVICE_TABLE(pci, cmodio_pci_ids); 271 272static struct pci_driver cmodio_pci_driver = { 273 .name = DRV_NAME, 274 .id_table = cmodio_pci_ids, 275 .probe = cmodio_pci_probe, 276 .remove = cmodio_pci_remove, 277}; 278 279module_pci_driver(cmodio_pci_driver); 280 281MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); 282MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver"); 283MODULE_LICENSE("GPL");