Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] superhyway: multiple block support and VCR rework

This extends the API somewhat to allow for platform-specific VCR reading and
writing. Some platforms (like SH4-202) implement the VCR in a split VCRL and
VCRH, but end up being in reverse order or have other quirks that need to be
dealt with, so we add a set of superhyway_ops per-bus to accomodate this.

We also have to extend the per-device resources somewhat, as some devices now
conveniently split control and data blocks. So we allow a platform to
register its set of SuperHyway devices via superhyway_add_devices() with the
control block always ordered as the first resource (as this is the one that
userspace cares about).

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Paul Mundt and committed by
Linus Torvalds
055a2512 72777373

+89 -26
+1 -1
drivers/sh/superhyway/superhyway-sysfs.c
··· 30 30 superhyway_ro_attr(top_mb, "0x%02x\n", vcr.top_mb); 31 31 32 32 /* Misc */ 33 - superhyway_ro_attr(resource, "0x%08lx\n", resource.start); 33 + superhyway_ro_attr(resource, "0x%08lx\n", resource[0].start); 34 34 35 35 struct device_attribute superhyway_dev_attrs[] = { 36 36 __ATTR_RO(perr_flags),
+55 -20
drivers/sh/superhyway/superhyway.c
··· 27 27 28 28 static void superhyway_device_release(struct device *dev) 29 29 { 30 - kfree(to_superhyway_device(dev)); 30 + struct superhyway_device *sdev = to_superhyway_device(dev); 31 + 32 + kfree(sdev->resource); 33 + kfree(sdev); 31 34 } 32 35 33 36 /** 34 37 * superhyway_add_device - Add a SuperHyway module 35 - * @mod_id: Module ID (taken from MODULE.VCR.MOD_ID). 36 38 * @base: Physical address where module is mapped. 37 - * @vcr: VCR value. 39 + * @sdev: SuperHyway device to add, or NULL to allocate a new one. 40 + * @bus: Bus where SuperHyway module resides. 38 41 * 39 42 * This is responsible for adding a new SuperHyway module. This sets up a new 40 - * struct superhyway_device for the module being added. Each one of @mod_id, 41 - * @base, and @vcr are registered with the new device for further use 42 - * elsewhere. 43 + * struct superhyway_device for the module being added if @sdev == NULL. 43 44 * 44 45 * Devices are initially added in the order that they are scanned (from the 45 46 * top-down of the memory map), and are assigned an ID based on the order that ··· 50 49 * Further work can and should be done in superhyway_scan_bus(), to be sure 51 50 * that any new modules are properly discovered and subsequently registered. 52 51 */ 53 - int superhyway_add_device(unsigned int mod_id, unsigned long base, 54 - unsigned long long vcr) 52 + int superhyway_add_device(unsigned long base, struct superhyway_device *sdev, 53 + struct superhyway_bus *bus) 55 54 { 56 - struct superhyway_device *dev; 55 + struct superhyway_device *dev = sdev; 57 56 58 - dev = kmalloc(sizeof(struct superhyway_device), GFP_KERNEL); 59 - if (!dev) 60 - return -ENOMEM; 57 + if (!dev) { 58 + dev = kmalloc(sizeof(struct superhyway_device), GFP_KERNEL); 59 + if (!dev) 60 + return -ENOMEM; 61 61 62 - memset(dev, 0, sizeof(struct superhyway_device)); 62 + memset(dev, 0, sizeof(struct superhyway_device)); 63 + } 63 64 64 - dev->id.id = mod_id; 65 - sprintf(dev->name, "SuperHyway device %04x", dev->id.id); 65 + dev->bus = bus; 66 + superhyway_read_vcr(dev, base, &dev->vcr); 66 67 67 - dev->vcr = *((struct vcr_info *)(&vcr)); 68 - dev->resource.name = dev->name; 69 - dev->resource.start = base; 70 - dev->resource.end = dev->resource.start + 0x01000000; 68 + if (!dev->resource) { 69 + dev->resource = kmalloc(sizeof(struct resource), GFP_KERNEL); 70 + if (!dev->resource) { 71 + kfree(dev); 72 + return -ENOMEM; 73 + } 74 + 75 + dev->resource->name = dev->name; 76 + dev->resource->start = base; 77 + dev->resource->end = dev->resource->start + 0x01000000; 78 + } 79 + 71 80 dev->dev.parent = &superhyway_bus_device; 72 81 dev->dev.bus = &superhyway_bus_type; 73 82 dev->dev.release = superhyway_device_release; 83 + dev->id.id = dev->vcr.mod_id; 74 84 85 + sprintf(dev->name, "SuperHyway device %04x", dev->id.id); 75 86 sprintf(dev->dev.bus_id, "%02x", superhyway_devices); 76 87 77 88 superhyway_devices++; ··· 91 78 return device_register(&dev->dev); 92 79 } 93 80 81 + int superhyway_add_devices(struct superhyway_bus *bus, 82 + struct superhyway_device **devices, 83 + int nr_devices) 84 + { 85 + int i, ret = 0; 86 + 87 + for (i = 0; i < nr_devices; i++) { 88 + struct superhyway_device *dev = devices[i]; 89 + ret |= superhyway_add_device(dev->resource[0].start, dev, bus); 90 + } 91 + 92 + return ret; 93 + } 94 + 94 95 static int __init superhyway_init(void) 95 96 { 97 + struct superhyway_bus *bus; 98 + int ret = 0; 99 + 96 100 device_register(&superhyway_bus_device); 97 - return superhyway_scan_bus(); 101 + 102 + for (bus = superhyway_channels; bus->ops; bus++) 103 + ret |= superhyway_scan_bus(bus); 104 + 105 + return ret; 98 106 } 99 107 100 108 postcore_initcall(superhyway_init); ··· 231 197 232 198 EXPORT_SYMBOL(superhyway_bus_type); 233 199 EXPORT_SYMBOL(superhyway_add_device); 200 + EXPORT_SYMBOL(superhyway_add_devices); 234 201 EXPORT_SYMBOL(superhyway_register_driver); 235 202 EXPORT_SYMBOL(superhyway_unregister_driver); 236 203
+33 -5
include/linux/superhyway.h
··· 19 19 */ 20 20 #define SUPERHYWAY_DEVICE_ID_SH5_DMAC 0x0183 21 21 22 - struct vcr_info { 22 + struct superhyway_vcr_info { 23 23 u8 perr_flags; /* P-port Error flags */ 24 24 u8 merr_flags; /* Module Error flags */ 25 25 u16 mod_vers; /* Module Version */ ··· 27 27 u8 bot_mb; /* Bottom Memory block */ 28 28 u8 top_mb; /* Top Memory block */ 29 29 }; 30 + 31 + struct superhyway_ops { 32 + int (*read_vcr)(unsigned long base, struct superhyway_vcr_info *vcr); 33 + int (*write_vcr)(unsigned long base, struct superhyway_vcr_info vcr); 34 + }; 35 + 36 + struct superhyway_bus { 37 + struct superhyway_ops *ops; 38 + }; 39 + 40 + extern struct superhyway_bus superhyway_channels[]; 30 41 31 42 struct superhyway_device_id { 32 43 unsigned int id; ··· 66 55 67 56 struct superhyway_device_id id; 68 57 struct superhyway_driver *drv; 58 + struct superhyway_bus *bus; 69 59 70 - struct resource resource; 71 - struct vcr_info vcr; 60 + int num_resources; 61 + struct resource *resource; 62 + struct superhyway_vcr_info vcr; 72 63 }; 73 64 74 65 #define to_superhyway_device(d) container_of((d), struct superhyway_device, dev) ··· 78 65 #define superhyway_get_drvdata(d) dev_get_drvdata(&(d)->dev) 79 66 #define superhyway_set_drvdata(d,p) dev_set_drvdata(&(d)->dev, (p)) 80 67 81 - extern int superhyway_scan_bus(void); 68 + static inline int 69 + superhyway_read_vcr(struct superhyway_device *dev, unsigned long base, 70 + struct superhyway_vcr_info *vcr) 71 + { 72 + return dev->bus->ops->read_vcr(base, vcr); 73 + } 74 + 75 + static inline int 76 + superhyway_write_vcr(struct superhyway_device *dev, unsigned long base, 77 + struct superhyway_vcr_info vcr) 78 + { 79 + return dev->bus->ops->write_vcr(base, vcr); 80 + } 81 + 82 + extern int superhyway_scan_bus(struct superhyway_bus *); 82 83 83 84 /* drivers/sh/superhyway/superhyway.c */ 84 85 int superhyway_register_driver(struct superhyway_driver *); 85 86 void superhyway_unregister_driver(struct superhyway_driver *); 86 - int superhyway_add_device(unsigned int, unsigned long, unsigned long long); 87 + int superhyway_add_device(unsigned long base, struct superhyway_device *, struct superhyway_bus *); 88 + int superhyway_add_devices(struct superhyway_bus *bus, struct superhyway_device **devices, int nr_devices); 87 89 88 90 /* drivers/sh/superhyway/superhyway-sysfs.c */ 89 91 extern struct device_attribute superhyway_dev_attrs[];