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