284 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Janz CMOD-IO MODULbus Carrier Board PCI Driver4 *5 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>6 *7 * Lots of inspiration and code was copied from drivers/mfd/sm501.c8 */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 0x20025 26/* Maximum number of MODULbus modules on a CMOD-IO carrier board */27#define CMODIO_MAX_MODULES 428 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 API59 */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 * IRQ103 *104 * The start and end fields are used as an offset to the irq_base105 * parameter passed into the mfd_add_devices() function call. All106 * 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 Attributes150 */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 Driver173 */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 its222 * own interrupt line if needed223 */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 0x13c3259 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");284