aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIra W. Snyder <iws@ovro.caltech.edu>2010-04-07 03:43:00 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2010-05-27 19:37:32 -0400
commitbd3581323cc02aefc79a21780a4ca8c578642892 (patch)
treeeccf8b03e14bdd16416fa30205c61c128e7ce3ce
parent165bce9783ab307368e56b7c1a168520a3791266 (diff)
mfd: Janz CMOD-IO PCI MODULbus Carrier Board support
The Janz CMOD-IO PCI MODULbus carrier board is a PCI to MODULbus bridge, which may host many different types of MODULbus daughterboards, including CAN and GPIO controllers. Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu> Reviewed-by: Wolfgang Grandegger <wg@grandegger.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/janz-cmodio.c304
-rw-r--r--include/linux/mfd/janz.h54
4 files changed, 369 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 93d0e31c0438..05c137d16b04 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -418,6 +418,16 @@ config MFD_RDC321X
418 southbridge which provides access to GPIOs and Watchdog using the 418 southbridge which provides access to GPIOs and Watchdog using the
419 southbridge PCI device configuration space. 419 southbridge PCI device configuration space.
420 420
421config MFD_JANZ_CMODIO
422 tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
423 select MFD_CORE
424 depends on PCI
425 help
426 This is the core driver for the Janz CMOD-IO PCI MODULbus
427 carrier board. This device is a PCI to MODULbus bridge which may
428 host many different types of MODULbus daughterboards, including
429 CAN and GPIO controllers.
430
421endmenu 431endmenu
422 432
423menu "Multimedia Capabilities Port drivers" 433menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f177e064b31b..21eff85b288a 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -65,3 +65,4 @@ obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
65obj-$(CONFIG_PMIC_ADP5520) += adp5520.o 65obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
66obj-$(CONFIG_LPC_SCH) += lpc_sch.o 66obj-$(CONFIG_LPC_SCH) += lpc_sch.o
67obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o 67obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
68obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
new file mode 100644
index 000000000000..9ed630799acc
--- /dev/null
+++ b/drivers/mfd/janz-cmodio.c
@@ -0,0 +1,304 @@
1/*
2 * Janz CMOD-IO MODULbus Carrier Board PCI Driver
3 *
4 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
5 *
6 * Lots of inspiration and code was copied from drivers/mfd/sm501.c
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/pci.h>
18#include <linux/interrupt.h>
19#include <linux/delay.h>
20#include <linux/platform_device.h>
21#include <linux/mfd/core.h>
22
23#include <linux/mfd/janz.h>
24
25#define DRV_NAME "janz-cmodio"
26
27/* Size of each MODULbus module in PCI BAR4 */
28#define CMODIO_MODULBUS_SIZE 0x200
29
30/* Maximum number of MODULbus modules on a CMOD-IO carrier board */
31#define CMODIO_MAX_MODULES 4
32
33/* Module Parameters */
34static unsigned int num_modules = CMODIO_MAX_MODULES;
35static unsigned char *modules[CMODIO_MAX_MODULES] = {
36 "empty", "empty", "empty", "empty",
37};
38
39module_param_array(modules, charp, &num_modules, S_IRUGO);
40MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board");
41
42/* Unique Device Id */
43static unsigned int cmodio_id;
44
45struct cmodio_device {
46 /* Parent PCI device */
47 struct pci_dev *pdev;
48
49 /* PLX control registers */
50 struct janz_cmodio_onboard_regs __iomem *ctrl;
51
52 /* hex switch position */
53 u8 hex;
54
55 /* mfd-core API */
56 struct mfd_cell cells[CMODIO_MAX_MODULES];
57 struct resource resources[3 * CMODIO_MAX_MODULES];
58 struct janz_platform_data pdata[CMODIO_MAX_MODULES];
59};
60
61/*
62 * Subdevices using the mfd-core API
63 */
64
65static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv,
66 char *name, unsigned int devno,
67 unsigned int modno)
68{
69 struct janz_platform_data *pdata;
70 struct mfd_cell *cell;
71 struct resource *res;
72 struct pci_dev *pci;
73
74 pci = priv->pdev;
75 cell = &priv->cells[devno];
76 res = &priv->resources[devno * 3];
77 pdata = &priv->pdata[devno];
78
79 cell->name = name;
80 cell->resources = res;
81 cell->num_resources = 3;
82
83 /* Setup the subdevice ID -- must be unique */
84 cell->id = cmodio_id++;
85
86 /* Add platform data */
87 pdata->modno = modno;
88 cell->platform_data = pdata;
89 cell->data_size = sizeof(*pdata);
90
91 /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */
92 res->flags = IORESOURCE_MEM;
93 res->parent = &pci->resource[3];
94 res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno);
95 res->end = res->start + CMODIO_MODULBUS_SIZE - 1;
96 res++;
97
98 /* PLX Control Registers -- PCI BAR4 is interrupt and other registers */
99 res->flags = IORESOURCE_MEM;
100 res->parent = &pci->resource[4];
101 res->start = pci->resource[4].start;
102 res->end = pci->resource[4].end;
103 res++;
104
105 /*
106 * IRQ
107 *
108 * The start and end fields are used as an offset to the irq_base
109 * parameter passed into the mfd_add_devices() function call. All
110 * devices share the same IRQ.
111 */
112 res->flags = IORESOURCE_IRQ;
113 res->parent = NULL;
114 res->start = 0;
115 res->end = 0;
116 res++;
117
118 return 0;
119}
120
121/* Probe each submodule using kernel parameters */
122static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
123{
124 struct pci_dev *pdev = priv->pdev;
125 unsigned int num_probed = 0;
126 char *name;
127 int i;
128
129 for (i = 0; i < num_modules; i++) {
130 name = modules[i];
131 if (!strcmp(name, "") || !strcmp(name, "empty"))
132 continue;
133
134 dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name);
135 cmodio_setup_subdevice(priv, name, num_probed, i);
136 num_probed++;
137 }
138
139 /* print an error message if no modules were probed */
140 if (num_probed == 0) {
141 dev_err(&priv->pdev->dev, "no MODULbus modules specified, "
142 "please set the ``modules'' kernel "
143 "parameter according to your "
144 "hardware configuration\n");
145 return -ENODEV;
146 }
147
148 return mfd_add_devices(&pdev->dev, 0, priv->cells,
149 num_probed, NULL, pdev->irq);
150}
151
152/*
153 * SYSFS Attributes
154 */
155
156static ssize_t mbus_show(struct device *dev, struct device_attribute *attr,
157 char *buf)
158{
159 struct cmodio_device *priv = dev_get_drvdata(dev);
160
161 return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex);
162}
163
164static DEVICE_ATTR(modulbus_number, S_IRUGO, mbus_show, NULL);
165
166static struct attribute *cmodio_sysfs_attrs[] = {
167 &dev_attr_modulbus_number.attr,
168 NULL,
169};
170
171static const struct attribute_group cmodio_sysfs_attr_group = {
172 .attrs = cmodio_sysfs_attrs,
173};
174
175/*
176 * PCI Driver
177 */
178
179static int __devinit cmodio_pci_probe(struct pci_dev *dev,
180 const struct pci_device_id *id)
181{
182 struct cmodio_device *priv;
183 int ret;
184
185 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
186 if (!priv) {
187 dev_err(&dev->dev, "unable to allocate private data\n");
188 ret = -ENOMEM;
189 goto out_return;
190 }
191
192 pci_set_drvdata(dev, priv);
193 priv->pdev = dev;
194
195 /* Hardware Initialization */
196 ret = pci_enable_device(dev);
197 if (ret) {
198 dev_err(&dev->dev, "unable to enable device\n");
199 goto out_free_priv;
200 }
201
202 pci_set_master(dev);
203 ret = pci_request_regions(dev, DRV_NAME);
204 if (ret) {
205 dev_err(&dev->dev, "unable to request regions\n");
206 goto out_pci_disable_device;
207 }
208
209 /* Onboard configuration registers */
210 priv->ctrl = pci_ioremap_bar(dev, 4);
211 if (!priv->ctrl) {
212 dev_err(&dev->dev, "unable to remap onboard regs\n");
213 ret = -ENOMEM;
214 goto out_pci_release_regions;
215 }
216
217 /* Read the hex switch on the carrier board */
218 priv->hex = ioread8(&priv->ctrl->int_enable);
219
220 /* Add the MODULbus number (hex switch value) to the device's sysfs */
221 ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
222 if (ret) {
223 dev_err(&dev->dev, "unable to create sysfs attributes\n");
224 goto out_unmap_ctrl;
225 }
226
227 /*
228 * Disable all interrupt lines, each submodule will enable its
229 * own interrupt line if needed
230 */
231 iowrite8(0xf, &priv->ctrl->int_disable);
232
233 /* Register drivers for all submodules */
234 ret = cmodio_probe_submodules(priv);
235 if (ret) {
236 dev_err(&dev->dev, "unable to probe submodules\n");
237 goto out_sysfs_remove_group;
238 }
239
240 return 0;
241
242out_sysfs_remove_group:
243 sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
244out_unmap_ctrl:
245 iounmap(priv->ctrl);
246out_pci_release_regions:
247 pci_release_regions(dev);
248out_pci_disable_device:
249 pci_disable_device(dev);
250out_free_priv:
251 kfree(priv);
252out_return:
253 return ret;
254}
255
256static void __devexit cmodio_pci_remove(struct pci_dev *dev)
257{
258 struct cmodio_device *priv = pci_get_drvdata(dev);
259
260 mfd_remove_devices(&dev->dev);
261 sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
262 iounmap(priv->ctrl);
263 pci_release_regions(dev);
264 pci_disable_device(dev);
265 kfree(priv);
266}
267
268#define PCI_VENDOR_ID_JANZ 0x13c3
269
270/* The list of devices that this module will support */
271static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = {
272 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 },
273 { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 },
274 { 0, }
275};
276MODULE_DEVICE_TABLE(pci, cmodio_pci_ids);
277
278static struct pci_driver cmodio_pci_driver = {
279 .name = DRV_NAME,
280 .id_table = cmodio_pci_ids,
281 .probe = cmodio_pci_probe,
282 .remove = __devexit_p(cmodio_pci_remove),
283};
284
285/*
286 * Module Init / Exit
287 */
288
289static int __init cmodio_init(void)
290{
291 return pci_register_driver(&cmodio_pci_driver);
292}
293
294static void __exit cmodio_exit(void)
295{
296 pci_unregister_driver(&cmodio_pci_driver);
297}
298
299MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
300MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver");
301MODULE_LICENSE("GPL");
302
303module_init(cmodio_init);
304module_exit(cmodio_exit);
diff --git a/include/linux/mfd/janz.h b/include/linux/mfd/janz.h
new file mode 100644
index 000000000000..e9994c469803
--- /dev/null
+++ b/include/linux/mfd/janz.h
@@ -0,0 +1,54 @@
1/*
2 * Common Definitions for Janz MODULbus devices
3 *
4 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#ifndef JANZ_H
13#define JANZ_H
14
15struct janz_platform_data {
16 /* MODULbus Module Number */
17 unsigned int modno;
18};
19
20/* PLX bridge chip onboard registers */
21struct janz_cmodio_onboard_regs {
22 u8 unused1;
23
24 /*
25 * Read access: interrupt status
26 * Write access: interrupt disable
27 */
28 u8 int_disable;
29 u8 unused2;
30
31 /*
32 * Read access: MODULbus number (hex switch)
33 * Write access: interrupt enable
34 */
35 u8 int_enable;
36 u8 unused3;
37
38 /* write-only */
39 u8 reset_assert;
40 u8 unused4;
41
42 /* write-only */
43 u8 reset_deassert;
44 u8 unused5;
45
46 /* read-write access to serial EEPROM */
47 u8 eep;
48 u8 unused6;
49
50 /* write-only access to EEPROM chip select */
51 u8 enid;
52};
53
54#endif /* JANZ_H */