diff options
author | Samuel Iglesias Gonsalvez <siglesias@igalia.com> | 2012-05-09 09:27:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-09 17:05:45 -0400 |
commit | d3465872c5b38613fb5ad10a9756db9372630b22 (patch) | |
tree | 5f60af8e14bf6f9f2657a4ce834c54feb17d4f99 | |
parent | de8fe0233f078e649d7c293218119234606c4546 (diff) |
Staging: IndustryPack bus for the Linux Kernel
Add IndustryPack bus support for the Linux Kernel.
This is a virtual bus that allows to perform all the operations between
carrier and mezzanine boards.
Signed-off-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/ipack/Kconfig | 9 | ||||
-rw-r--r-- | drivers/staging/ipack/Makefile | 4 | ||||
-rw-r--r-- | drivers/staging/ipack/TODO | 21 | ||||
-rw-r--r-- | drivers/staging/ipack/ipack.c | 175 | ||||
-rw-r--r-- | drivers/staging/ipack/ipack.h | 183 |
7 files changed, 395 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 32b02e443ed3..781cb98597ca 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig | |||
@@ -24,6 +24,8 @@ menuconfig STAGING | |||
24 | 24 | ||
25 | if STAGING | 25 | if STAGING |
26 | 26 | ||
27 | source "drivers/staging/ipack/Kconfig" | ||
28 | |||
27 | source "drivers/staging/et131x/Kconfig" | 29 | source "drivers/staging/et131x/Kconfig" |
28 | 30 | ||
29 | source "drivers/staging/slicoss/Kconfig" | 31 | source "drivers/staging/slicoss/Kconfig" |
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e603c0756960..fd8b7ce3a221 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile | |||
@@ -30,6 +30,7 @@ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ | |||
30 | obj-$(CONFIG_VT6655) += vt6655/ | 30 | obj-$(CONFIG_VT6655) += vt6655/ |
31 | obj-$(CONFIG_VT6656) += vt6656/ | 31 | obj-$(CONFIG_VT6656) += vt6656/ |
32 | obj-$(CONFIG_VME_BUS) += vme/ | 32 | obj-$(CONFIG_VME_BUS) += vme/ |
33 | obj-$(CONFIG_IPACK_BUS) += ipack/ | ||
33 | obj-$(CONFIG_DX_SEP) += sep/ | 34 | obj-$(CONFIG_DX_SEP) += sep/ |
34 | obj-$(CONFIG_IIO) += iio/ | 35 | obj-$(CONFIG_IIO) += iio/ |
35 | obj-$(CONFIG_ZRAM) += zram/ | 36 | obj-$(CONFIG_ZRAM) += zram/ |
diff --git a/drivers/staging/ipack/Kconfig b/drivers/staging/ipack/Kconfig new file mode 100644 index 000000000000..e20187f6c268 --- /dev/null +++ b/drivers/staging/ipack/Kconfig | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # IPACK configuration. | ||
3 | # | ||
4 | |||
5 | menuconfig IPACK_BUS | ||
6 | tristate "IndustryPack bus support" | ||
7 | ---help--- | ||
8 | If you say Y here you get support for the IndustryPack Framework. | ||
9 | |||
diff --git a/drivers/staging/ipack/Makefile b/drivers/staging/ipack/Makefile new file mode 100644 index 000000000000..56e2340d3660 --- /dev/null +++ b/drivers/staging/ipack/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | # | ||
2 | # Makefile for the IPACK bridge device drivers. | ||
3 | # | ||
4 | obj-$(CONFIG_IPACK_BUS) += ipack.o | ||
diff --git a/drivers/staging/ipack/TODO b/drivers/staging/ipack/TODO new file mode 100644 index 000000000000..167ae4daf486 --- /dev/null +++ b/drivers/staging/ipack/TODO | |||
@@ -0,0 +1,21 @@ | |||
1 | TODO | ||
2 | ==== | ||
3 | Introduction | ||
4 | ============ | ||
5 | |||
6 | These drivers add support for IndustryPack devices: carrier and mezzanine | ||
7 | boards. | ||
8 | |||
9 | The ipack driver is just an abstraction of the bus providing the common | ||
10 | operations between the two kind of boards. | ||
11 | |||
12 | TODO | ||
13 | ==== | ||
14 | |||
15 | Ipack | ||
16 | ----- | ||
17 | |||
18 | * The structures and API exported can be improved a lot. For example, the | ||
19 | way to unregistering mezzanine devices, doing the mezzanine driver a call to | ||
20 | remove_device() to notify the carrier driver, or the opposite with the call to | ||
21 | the ipack_driver_ops' remove() function could be improved. | ||
diff --git a/drivers/staging/ipack/ipack.c b/drivers/staging/ipack/ipack.c new file mode 100644 index 000000000000..a54bfd7c25e1 --- /dev/null +++ b/drivers/staging/ipack/ipack.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * Industry-pack bus support functions. | ||
3 | * | ||
4 | * (C) 2011 Samuel Iglesias Gonsalvez <siglesia@cern.ch>, CERN | ||
5 | * (C) 2012 Samuel Iglesias Gonsalvez <siglesias@igalia.com>, Igalia | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the Free | ||
9 | * Software Foundation; either version 2 of the License, or (at your option) | ||
10 | * any later version. | ||
11 | */ | ||
12 | |||
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/mutex.h> | ||
17 | #include "ipack.h" | ||
18 | |||
19 | #define to_ipack_dev(device) container_of(device, struct ipack_device, dev) | ||
20 | #define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver) | ||
21 | |||
22 | /* used when allocating bus numbers */ | ||
23 | #define IPACK_MAXBUS 64 | ||
24 | |||
25 | static DEFINE_MUTEX(ipack_mutex); | ||
26 | |||
27 | struct ipack_busmap { | ||
28 | unsigned long busmap[IPACK_MAXBUS / (8*sizeof(unsigned long))]; | ||
29 | }; | ||
30 | static struct ipack_busmap busmap; | ||
31 | |||
32 | static int ipack_bus_match(struct device *device, struct device_driver *driver) | ||
33 | { | ||
34 | int ret; | ||
35 | struct ipack_device *dev = to_ipack_dev(device); | ||
36 | struct ipack_driver *drv = to_ipack_driver(driver); | ||
37 | |||
38 | if (!drv->ops->match) | ||
39 | return -EINVAL; | ||
40 | |||
41 | ret = drv->ops->match(dev); | ||
42 | if (ret) | ||
43 | dev->driver = drv; | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int ipack_bus_probe(struct device *device) | ||
49 | { | ||
50 | struct ipack_device *dev = to_ipack_dev(device); | ||
51 | |||
52 | if (!dev->driver->ops->probe) | ||
53 | return -EINVAL; | ||
54 | |||
55 | return dev->driver->ops->probe(dev); | ||
56 | } | ||
57 | |||
58 | static int ipack_bus_remove(struct device *device) | ||
59 | { | ||
60 | struct ipack_device *dev = to_ipack_dev(device); | ||
61 | |||
62 | if (!dev->driver->ops->remove) | ||
63 | return -EINVAL; | ||
64 | |||
65 | dev->driver->ops->remove(dev); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static struct bus_type ipack_bus_type = { | ||
70 | .name = "ipack", | ||
71 | .probe = ipack_bus_probe, | ||
72 | .match = ipack_bus_match, | ||
73 | .remove = ipack_bus_remove, | ||
74 | }; | ||
75 | |||
76 | static int ipack_assign_bus_number(void) | ||
77 | { | ||
78 | int busnum; | ||
79 | |||
80 | mutex_lock(&ipack_mutex); | ||
81 | busnum = find_next_zero_bit(busmap.busmap, IPACK_MAXBUS, 1); | ||
82 | |||
83 | if (busnum >= IPACK_MAXBUS) { | ||
84 | pr_err("too many buses\n"); | ||
85 | busnum = -1; | ||
86 | goto error_find_busnum; | ||
87 | } | ||
88 | |||
89 | set_bit(busnum, busmap.busmap); | ||
90 | |||
91 | error_find_busnum: | ||
92 | mutex_unlock(&ipack_mutex); | ||
93 | return busnum; | ||
94 | } | ||
95 | |||
96 | int ipack_bus_register(struct ipack_bus_device *bus) | ||
97 | { | ||
98 | int bus_nr; | ||
99 | |||
100 | bus_nr = ipack_assign_bus_number(); | ||
101 | if (bus_nr < 0) | ||
102 | return -1; | ||
103 | |||
104 | bus->bus_nr = bus_nr; | ||
105 | return 0; | ||
106 | } | ||
107 | EXPORT_SYMBOL_GPL(ipack_bus_register); | ||
108 | |||
109 | int ipack_bus_unregister(struct ipack_bus_device *bus) | ||
110 | { | ||
111 | mutex_lock(&ipack_mutex); | ||
112 | clear_bit(bus->bus_nr, busmap.busmap); | ||
113 | mutex_unlock(&ipack_mutex); | ||
114 | return 0; | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(ipack_bus_unregister); | ||
117 | |||
118 | int ipack_driver_register(struct ipack_driver *edrv) | ||
119 | { | ||
120 | edrv->driver.bus = &ipack_bus_type; | ||
121 | return driver_register(&edrv->driver); | ||
122 | } | ||
123 | EXPORT_SYMBOL_GPL(ipack_driver_register); | ||
124 | |||
125 | void ipack_driver_unregister(struct ipack_driver *edrv) | ||
126 | { | ||
127 | driver_unregister(&edrv->driver); | ||
128 | } | ||
129 | EXPORT_SYMBOL_GPL(ipack_driver_unregister); | ||
130 | |||
131 | static void ipack_device_release(struct device *dev) | ||
132 | { | ||
133 | } | ||
134 | |||
135 | int ipack_device_register(struct ipack_device *dev) | ||
136 | { | ||
137 | int ret; | ||
138 | |||
139 | dev->dev.bus = &ipack_bus_type; | ||
140 | dev->dev.release = ipack_device_release; | ||
141 | dev_set_name(&dev->dev, | ||
142 | "%s.%u.%u", dev->board_name, dev->bus_nr, dev->slot); | ||
143 | |||
144 | ret = device_register(&dev->dev); | ||
145 | if (ret < 0) { | ||
146 | pr_err("error registering the device.\n"); | ||
147 | dev->driver->ops->remove(dev); | ||
148 | } | ||
149 | |||
150 | return ret; | ||
151 | } | ||
152 | EXPORT_SYMBOL_GPL(ipack_device_register); | ||
153 | |||
154 | void ipack_device_unregister(struct ipack_device *dev) | ||
155 | { | ||
156 | device_unregister(&dev->dev); | ||
157 | } | ||
158 | EXPORT_SYMBOL_GPL(ipack_device_unregister); | ||
159 | |||
160 | static int __init ipack_init(void) | ||
161 | { | ||
162 | return bus_register(&ipack_bus_type); | ||
163 | } | ||
164 | |||
165 | static void __exit ipack_exit(void) | ||
166 | { | ||
167 | bus_unregister(&ipack_bus_type); | ||
168 | } | ||
169 | |||
170 | module_init(ipack_init); | ||
171 | module_exit(ipack_exit); | ||
172 | |||
173 | MODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>"); | ||
174 | MODULE_LICENSE("GPL"); | ||
175 | MODULE_DESCRIPTION("Industry-pack bus core"); | ||
diff --git a/drivers/staging/ipack/ipack.h b/drivers/staging/ipack/ipack.h new file mode 100644 index 000000000000..41d61726f317 --- /dev/null +++ b/drivers/staging/ipack/ipack.h | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Industry-pack bus. | ||
3 | * | ||
4 | * (C) 2011 Samuel Iglesias Gonsalvez <siglesia@cern.ch>, CERN | ||
5 | * (C) 2012 Samuel Iglesias Gonsalvez <siglesias@igalia.com>, Igalia | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the Free | ||
9 | * Software Foundation; either version 2 of the License, or (at your option) | ||
10 | * any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/device.h> | ||
14 | |||
15 | #define IPACK_BOARD_NAME_SIZE 16 | ||
16 | #define IPACK_IRQ_NAME_SIZE 50 | ||
17 | #define IPACK_IDPROM_OFFSET_I 0x01 | ||
18 | #define IPACK_IDPROM_OFFSET_P 0x03 | ||
19 | #define IPACK_IDPROM_OFFSET_A 0x05 | ||
20 | #define IPACK_IDPROM_OFFSET_C 0x07 | ||
21 | #define IPACK_IDPROM_OFFSET_MANUFACTURER_ID 0x09 | ||
22 | #define IPACK_IDPROM_OFFSET_MODEL 0x0B | ||
23 | #define IPACK_IDPROM_OFFSET_REVISION 0x0D | ||
24 | #define IPACK_IDPROM_OFFSET_RESERVED 0x0F | ||
25 | #define IPACK_IDPROM_OFFSET_DRIVER_ID_L 0x11 | ||
26 | #define IPACK_IDPROM_OFFSET_DRIVER_ID_H 0x13 | ||
27 | #define IPACK_IDPROM_OFFSET_NUM_BYTES 0x15 | ||
28 | #define IPACK_IDPROM_OFFSET_CRC 0x17 | ||
29 | |||
30 | struct ipack_bus_ops; | ||
31 | struct ipack_driver; | ||
32 | |||
33 | enum ipack_space { | ||
34 | IPACK_IO_SPACE = 0, | ||
35 | IPACK_ID_SPACE = 1, | ||
36 | IPACK_MEM_SPACE = 2, | ||
37 | }; | ||
38 | |||
39 | /** | ||
40 | * struct ipack_addr_space - Virtual address space mapped for a specified type. | ||
41 | * | ||
42 | * @address: virtual address | ||
43 | * @size: size of the mapped space | ||
44 | */ | ||
45 | struct ipack_addr_space { | ||
46 | void *address; | ||
47 | unsigned int size; | ||
48 | }; | ||
49 | |||
50 | /** | ||
51 | * struct ipack_device | ||
52 | * | ||
53 | * @board_name: IP mezzanine board name | ||
54 | * @bus_name: IP carrier board name | ||
55 | * @bus_nr: IP bus number where the device is plugged | ||
56 | * @slot: Slot where the device is plugged in the carrier board | ||
57 | * @irq: IRQ vector | ||
58 | * @driver: Pointer to the ipack_driver that manages the device | ||
59 | * @ops: Carrier board operations to access the device | ||
60 | * @id_space: Virtual address to ID space. | ||
61 | * @io_space: Virtual address to IO space. | ||
62 | * @mem_space: Virtual address to MEM space. | ||
63 | * @dev: device in kernel representation. | ||
64 | * | ||
65 | * Warning: Direct access to mapped memory is possible but the endianness | ||
66 | * is not the same with PCI carrier or VME carrier. The endianness is managed | ||
67 | * by the carrier board throught @ops. | ||
68 | */ | ||
69 | struct ipack_device { | ||
70 | char board_name[IPACK_BOARD_NAME_SIZE]; | ||
71 | char bus_name[IPACK_BOARD_NAME_SIZE]; | ||
72 | unsigned int bus_nr; | ||
73 | unsigned int slot; | ||
74 | unsigned int irq; | ||
75 | struct ipack_driver *driver; | ||
76 | struct ipack_bus_ops *ops; | ||
77 | struct ipack_addr_space id_space; | ||
78 | struct ipack_addr_space io_space; | ||
79 | struct ipack_addr_space mem_space; | ||
80 | struct device dev; | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * struct ipack_driver_ops -- callbacks to mezzanine driver for installing/removing one device | ||
85 | * | ||
86 | * @match: Match function | ||
87 | * @probe: Probe function | ||
88 | * @remove: tell the driver that the carrier board wants to remove one device | ||
89 | */ | ||
90 | |||
91 | struct ipack_driver_ops { | ||
92 | int (*match) (struct ipack_device *dev); | ||
93 | int (*probe) (struct ipack_device *dev); | ||
94 | void (*remove) (struct ipack_device *dev); | ||
95 | }; | ||
96 | |||
97 | /** | ||
98 | * struct ipack_driver -- Specific data to each mezzanine board driver | ||
99 | * | ||
100 | * @driver: Device driver kernel representation | ||
101 | * @ops: Mezzanine driver operations specific for the ipack bus. | ||
102 | */ | ||
103 | struct ipack_driver { | ||
104 | struct module *owner; | ||
105 | struct device_driver driver; | ||
106 | struct ipack_driver_ops *ops; | ||
107 | }; | ||
108 | |||
109 | /* | ||
110 | * ipack_driver_register -- Register a new mezzanine driver | ||
111 | * | ||
112 | * Called by the mezzanine driver to register itself as a driver | ||
113 | * that can manage ipack devices. | ||
114 | */ | ||
115 | |||
116 | int ipack_driver_register(struct ipack_driver *edrv); | ||
117 | void ipack_driver_unregister(struct ipack_driver *edrv); | ||
118 | |||
119 | /* | ||
120 | * ipack_device_register -- register a new mezzanine device | ||
121 | * | ||
122 | * Register a new ipack device (mezzanine device). The call is done by | ||
123 | * the carrier device driver. | ||
124 | */ | ||
125 | int ipack_device_register(struct ipack_device *dev); | ||
126 | void ipack_device_unregister(struct ipack_device *dev); | ||
127 | |||
128 | /** | ||
129 | * struct ipack_bus_ops - available operations on a bridge module | ||
130 | * | ||
131 | * @map_space: map IP address space | ||
132 | * @unmap_space: unmap IP address space | ||
133 | * @request_irq: request IRQ | ||
134 | * @free_irq: free IRQ | ||
135 | * @read8: read unsigned char | ||
136 | * @read16: read unsigned short | ||
137 | * @read32: read unsigned int | ||
138 | * @write8: read unsigned char | ||
139 | * @write16: read unsigned short | ||
140 | * @write32: read unsigned int | ||
141 | * @remove_device: tell the bridge module that the device has been removed | ||
142 | */ | ||
143 | struct ipack_bus_ops { | ||
144 | int (*map_space) (struct ipack_device *dev, unsigned int memory_size, int space); | ||
145 | int (*unmap_space) (struct ipack_device *dev, int space); | ||
146 | int (*request_irq) (struct ipack_device *dev, int vector, int (*handler)(void *), void *arg); | ||
147 | int (*free_irq) (struct ipack_device *dev); | ||
148 | int (*read8) (struct ipack_device *dev, int space, unsigned long offset, unsigned char *value); | ||
149 | int (*read16) (struct ipack_device *dev, int space, unsigned long offset, unsigned short *value); | ||
150 | int (*read32) (struct ipack_device *dev, int space, unsigned long offset, unsigned int *value); | ||
151 | int (*write8) (struct ipack_device *dev, int space, unsigned long offset, unsigned char value); | ||
152 | int (*write16) (struct ipack_device *dev, int space, unsigned long offset, unsigned short value); | ||
153 | int (*write32) (struct ipack_device *dev, int space, unsigned long offset, unsigned int value); | ||
154 | int (*remove_device) (struct ipack_device *dev); | ||
155 | }; | ||
156 | |||
157 | /** | ||
158 | * struct ipack_bus_device | ||
159 | * | ||
160 | * @dev: pointer to carrier device | ||
161 | * @slots: number of slots available | ||
162 | * @bus_nr: ipack bus number | ||
163 | * @vector: IRQ base vector. IRQ vectors are $vector + $slot_number | ||
164 | */ | ||
165 | struct ipack_bus_device { | ||
166 | struct device *dev; | ||
167 | int slots; | ||
168 | int bus_nr; | ||
169 | int vector; | ||
170 | }; | ||
171 | |||
172 | /** | ||
173 | * ipack_bus_register -- register a new ipack bus | ||
174 | * | ||
175 | * The carrier board device driver should call this function to register itself | ||
176 | * as available bus in ipack. | ||
177 | */ | ||
178 | int ipack_bus_register(struct ipack_bus_device *bus); | ||
179 | |||
180 | /** | ||
181 | * ipack_bus_unregister -- unregister an ipack bus | ||
182 | */ | ||
183 | int ipack_bus_unregister(struct ipack_bus_device *bus); | ||