brintos

brintos / linux-shallow public Read only

0
0
Text · 7.0 KiB · add3bc0 Raw
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